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

> Discover the pattern for adding new API endpoints to a FastAPI application. Learn to create routers, implement services, define schemas, and register them efficiently in Open Notebook.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-14

---

**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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py). Delegate all business logic to service functions rather than implementing it directly in the router. The existing [`api/routers/notes.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notes.py) demonstrates this pattern with clean separation between HTTP status codes and data operations.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) or create a dedicated models file (e.g., [`api/my_feature_models.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) illustrate the expected patterns for validation.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/sources_service.py) for an example of database interaction and business logic encapsulation.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)

Import the new router at the top of [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notes.py)
- Define Pydantic models in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/sources_service.py)) rather than in routers
- Register new routers in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/sources_service.py).

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

Register the router in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) using `app.include_router()` after the authentication and CORS middleware are initialized. The registration order in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py)?

Yes, you can create dedicated model files (e.g., [`api/my_feature_models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/my_feature_models.py)) for complex features. Import these schemas into your router just as you would from [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py), following the Pydantic patterns demonstrated in existing models like `NoteCreate` and `NoteResponse` with proper `Field` definitions and type constraints.

### What is the recommended way to test new endpoints in Open Notebook?

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.