How to Add New API Endpoints to the FastAPI Application in Open Notebook

To add a new endpoint in Open Notebook's FastAPI application, create a router in api/routers/, implement the service layer in a dedicated module, define Pydantic schemas, and register the router in api/main.py using app.include_router().

Open Notebook organizes its HTTP API using a three-layer pattern that separates routing, business logic, and data validation. When you need to add new API endpoints to the FastAPI application, following this established structure ensures consistency with the existing codebase and automatic integration with authentication and CORS middleware. The pattern is explicitly documented in api/CLAUDE.md (lines 35-43) and implemented across the router files in the lfnovo/open-notebook repository.

The Three-Layer Architecture Pattern

Open Notebook structures its FastAPI application into three distinct layers: routers (HTTP handling), services (business logic), and models (Pydantic validation). This separation keeps HTTP concerns isolated from database queries and AI invocations. As implemented in lfnovo/open-notebook, the entry point at api/main.py wires all components together after initializing middleware, ensuring every registered endpoint inherits security and error-handling behaviors.

Step-by-Step Implementation Guide

1. Create the Router in api/routers/

Create a new Python file in api/routers/ (e.g., my_feature.py) and instantiate an APIRouter. Define path operations using decorators like @router.get() or @router.post(), importing request/response schemas from api/models.py. Delegate all business logic to service functions rather than implementing it directly in the router. The existing api/routers/notes.py demonstrates this pattern with clean separation between HTTP status codes and data operations.

from fastapi import APIRouter, HTTPException
from api.models import MyFeatureRequest, MyFeatureResponse
from api.my_feature_service import run_my_feature

router = APIRouter()

@router.post("/my-feature", response_model=MyFeatureResponse)
async def my_feature_endpoint(payload: MyFeatureRequest):
    try:
        result = await run_my_feature(payload)
        return MyFeatureResponse(**result)
    except Exception as exc:
        raise HTTPException(status_code=400, detail=str(exc))

2. Define Request and Response Models

Add Pydantic schemas to api/models.py or create a dedicated models file (e.g., api/my_feature_models.py). Use Field descriptions, validators, and Literal constraints to enforce type safety. The existing NoteCreate, NoteResponse, and NoteUpdate models in api/models.py illustrate the expected patterns for validation.

from pydantic import BaseModel, Field

class MyFeatureRequest(BaseModel):
    id: str = Field(..., description="Identifier of the resource")

class MyFeatureResponse(BaseModel):
    result: dict
    status: str

3. Implement the Service Layer

Create a service module (e.g., api/my_feature_service.py) containing async functions that handle core logic. These functions interact with SurrealDB through repo_query, invoke LangGraph graphs, or call external AI services. The router forwards validated input to these functions, keeping the HTTP layer thin. Reference api/sources_service.py for an example of database interaction and business logic encapsulation.

from open_notebook.database.repository import repo_query
from api.models import MyFeatureRequest

async def run_my_feature(req: MyFeatureRequest) -> dict:
    data = await repo_query("SELECT * FROM my_data WHERE id = $id", {"id": req.id})
    return {"result": data, "status": "ok"}

4. Register the Router in api/main.py

Import the new router at the top of api/main.py and register it with app.include_router(my_feature.router, tags=["my_feature"]). Registration occurs after CORS and authentication middleware are added, ensuring the new endpoint inherits these behaviors automatically. This centralizes route discovery and applies global exception handling.

from api.routers import my_feature

app.include_router(my_feature.router, tags=["my_feature"])

Testing and Verification

Launch the application with uv run uvicorn api.main:app --host 0.0.0.0 --port 5055 and verify the endpoint appears in the OpenAPI documentation at http://localhost:5055/docs. Unit tests should import service functions directly to bypass HTTP overhead, or use FastAPI's TestClient for integration testing against the actual router.

Summary

  • Create an APIRouter in api/routers/ to handle HTTP requests and responses, following the pattern in api/routers/notes.py
  • Define Pydantic models in api/models.py for request validation and serialization, using Field and validators as shown in existing schemas
  • Implement business logic in async service functions within dedicated modules (e.g., api/sources_service.py) rather than in routers
  • Register new routers in api/main.py using app.include_router() after middleware setup to inherit authentication and CORS behaviors
  • Test endpoints using the interactive docs at /docs or FastAPI's TestClient to verify correct integration

Frequently Asked Questions

Where should I place the business logic when adding a new FastAPI endpoint?

Place business logic in a dedicated service module (e.g., api/my_feature_service.py) rather than inside the router. This keeps the HTTP layer focused on request/response handling while service functions manage database queries through repo_query and AI integrations, as demonstrated in api/sources_service.py.

How do I ensure my new endpoint uses Open Notebook's authentication middleware?

Register the router in api/main.py using app.include_router() after the authentication and CORS middleware are initialized. The registration order in api/main.py ensures all registered routers inherit these security behaviors automatically, without requiring additional configuration in the router file.

Can I create separate model files instead of adding to api/models.py?

Yes, you can create dedicated model files (e.g., api/my_feature_models.py) for complex features. Import these schemas into your router just as you would from api/models.py, following the Pydantic patterns demonstrated in existing models like NoteCreate and NoteResponse with proper Field definitions and type constraints.

Use FastAPI's TestClient for endpoint integration tests or import service functions directly for unit testing. Verify implementation by running uv run uvicorn api.main:app --host 0.0.0.0 --port 5055 and checking the OpenAPI documentation at http://localhost:5055/docs to confirm the route appears with correct schemas and tags.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →