# How to Add a Custom API Endpoint in the FastAPI Backend (Open Notebook)

> Learn to add a custom API endpoint in FastAPI backend by creating routers, defining Pydantic models, and registering them in main.py. Extend your Open Notebook API effortlessly.

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

---

**To add a custom API endpoint in Open Notebook's FastAPI backend, create a new `APIRouter` in `api/routers/`, define Pydantic models in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py), and register the router in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) using `app.include_router()` with the `/api` prefix.**

Open Notebook is an open-source project that uses **FastAPI** to serve its REST API, following a modular architecture where endpoints are organized by functional area. Understanding the router pattern used in the existing codebase allows you to extend the API with custom endpoints that automatically inherit global middleware, CORS configuration, and authentication layers. The canonical implementation follows the pattern established in [`api/routers/notebooks.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notebooks.py) and wired together in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py).

## Understanding the Router Architecture

Open Notebook's API structure follows FastAPI's recommended best practices for larger applications:

- **Modular routers**: Each domain lives in its own module under `api/routers/`.
- **Centralized schema**: Request and response models are defined in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) using Pydantic.
- **Application factory**: The main FastAPI instance is constructed in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), where all routers are included with specific prefixes and tags.

In [`api/routers/notebooks.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notebooks.py), the router is instantiated on line 17 using `router = APIRouter()`, which serves as the reference pattern for new modules.

## Step-by-Step Implementation

### Create the Router File

Create a new Python file in the routers directory. The standard convention places new functionality in [`api/routers/custom.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/custom.py) (or a descriptive name matching your feature).

```python

# api/routers/custom.py

from fastapi import APIRouter, HTTPException
from api.models import ExampleResponse
from loguru import logger

router = APIRouter()

```

This mirrors the instantiation pattern found at the top of existing router files like [`api/routers/notebooks.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notebooks.py).

### Define Pydantic Schemas

Add your request and response models to [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) to ensure automatic validation and OpenAPI documentation generation:

```python

# api/models.py

from pydantic import BaseModel

class ExampleResponse(BaseModel):
    """Response model for the example endpoint."""
    message: str

```

Keeping schemas centralized in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) maintains consistency across the codebase and makes dependencies between routers easier to manage.

### Implement Endpoint Logic

Define your path operations using the standard FastAPI decorator syntax. The following example demonstrates a simple GET endpoint with query parameter handling:

```python

# api/routers/custom.py

@router.get("/custom/hello", response_model=ExampleResponse)
async def hello(name: str = "world"):
    """Return a friendly greeting."""
    logger.info(f"hello endpoint called with name={name}")
    return ExampleResponse(message=f"Hello, {name}!")

```

Open Notebook uses `loguru` for structured logging, as imported in the router setup above. Error handling should use `HTTPException` from FastAPI to return appropriate status codes.

### Register with the FastAPI App

Import and include your new router in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), following the pattern used for the notebook router on lines 92-94:

```python

# api/main.py

from api.routers import custom  # Add this import

# ... existing router registrations ...

app.include_router(custom.router, prefix="/api", tags=["custom"])

```

The `prefix="/api"` parameter ensures your endpoint appears under the `/api` path alongside existing endpoints. The `tags` parameter groups the endpoint under a named section in the auto-generated Swagger UI.

## Complete Working Example

Here is the full implementation required to add a functional custom endpoint:

```python

# ------------------------------

# api/models.py  (add this class)

# ------------------------------

from pydantic import BaseModel

class ExampleResponse(BaseModel):
    message: str

```

```python

# ------------------------------

# api/routers/custom.py  (create this file)

# ------------------------------

from fastapi import APIRouter, HTTPException
from api.models import ExampleResponse
from loguru import logger

router = APIRouter()

@router.get("/custom/hello", response_model=ExampleResponse)
async def hello(name: str = "world"):
    """Simple demo endpoint that returns a greeting."""
    logger.info(f"Received hello request for name={name}")
    return ExampleResponse(message=f"Hello, {name}!")

```

```python

# ------------------------------

# api/main.py  (add these lines)

# ------------------------------

from api.routers import custom  # New import

# After existing router registrations (around line 92-94)

app.include_router(custom.router, prefix="/api", tags=["custom"])

```

After implementation, restart the server using `uvicorn api.main:app` or the provided [`run_api.py`](https://github.com/lfnovo/open-notebook/blob/main/run_api.py) script. The new endpoint will be available at `http://localhost:5055/api/custom/hello` and documented at `http://localhost:5055/docs` under the **custom** tag.

## Key Files and References

| Path | Purpose |
|------|---------|
| [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) | Creates the FastAPI app, configures global CORS, and includes all routers at lines 92-94. |
| [`api/routers/notebooks.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notebooks.py) | Reference implementation showing the canonical router pattern (instantiation at line 17). |
| [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) | Central location for Pydantic request/response models. |
| [`api/routers/custom.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/custom.py) | Your new router file (to be created). |
| [`run_api.py`](https://github.com/lfnovo/open-notebook/blob/main/run_api.py) | Convenience script to launch the API with `uvicorn`. |

## Summary

- **Instantiate `APIRouter`** in a new file under `api/routers/` to encapsulate your endpoint logic.
- **Define Pydantic models** in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py) for automatic request validation and response serialization.
- **Register the router** in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) using `app.include_router()` with the `/api` prefix to maintain URL consistency.
- **Leverage existing infrastructure** such as `loguru` for logging and the global error handling middleware already configured in the FastAPI app.

## Frequently Asked Questions

### Where should I place my custom endpoint code in Open Notebook?

Create a new file inside `api/routers/` (e.g., [`api/routers/custom.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/custom.py)) and instantiate an `APIRouter` there. This keeps the codebase modular and follows the existing architectural pattern used by the notebook and other functional modules.

### How do I ensure my custom endpoint appears in the Swagger documentation?

Define Pydantic models for your request and response schemas in [`api/models.py`](https://github.com/lfnovo/open-notebook/blob/main/api/models.py), then reference them in your router using the `response_model` parameter. When you register the router in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) with a `tags` argument, the endpoints will appear grouped under that tag in the auto-generated Swagger UI at `/docs`.

### Do I need to manually configure CORS or authentication for my new endpoint?

No. When you register your router in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) using `app.include_router()`, the endpoint automatically inherits the global CORS middleware and authentication layers already configured in the FastAPI application instance. This centralized configuration in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) applies to all included routers.

### Can I use async/await in my custom endpoint functions?

Yes. Open Notebook's FastAPI backend fully supports async path operation functions. Use `async def` for your endpoint handlers, especially when performing I/O operations like database calls or external API requests, to ensure non-blocking execution.