How API Routes Are Registered and Organized with Tags in open-notebook's main.py

In lfnovo/open-notebook, every API route is registered inside api/main.py by importing modular APIRouter instances from api/routers/ and mounting them with app.include_router() using a shared /api prefix and a descriptive tags list that drives OpenAPI documentation grouping.

Managing a growing FastAPI backend requires a predictable registration pattern. In the lfnovo/open-notebook repository, the api/main.py file serves as the single source of truth for how API routes are registered and organized with tags, ensuring that Swagger UI stays clean and developers can locate endpoints quickly.

The Router Registry in api/main.py

According to the lfnovo/open-notebook source code, the FastAPI application instance is created in api/main.py. Rather than defining endpoints directly in that file, the project uses a modular router pattern that keeps the main bootstrap file focused solely on assembly.

All router modules live in the api/routers/ package and are imported in bulk near the top of api/main.py:

from api.routers import (
    auth, chat, config, context, credentials, embedding,
    embedding_rebuild, episode_profiles, insights, languages,
    models, notebooks, notes, podcasts, search, settings,
    source_chat, sources, speaker_profiles, transformations,
)
from api.routers import commands as commands_router

The commands router is imported separately as commands_router to avoid naming conflicts with the module itself.

After the FastAPI app is instantiated, each router is attached using the same consistent signature starting around line 89:

app.include_router(auth.router, prefix="/api", tags=["auth"])
app.include_router(config.router, prefix="/api", tags=["config"])
app.include_router(notebooks.router, prefix="/api", tags=["notebooks"])

# ... continues for all routers

The prefix="/api" argument guarantees that every endpoint in every router is mounted under the /api base path. The tags argument is a list that tells FastAPI how to group routes in the generated OpenAPI schema and the interactive Swagger UI.

Tag Mapping and OpenAPI Grouping

Because each call to app.include_router() supplies its own tags list, the resulting /docs page automatically organizes endpoints by functional area. Developers do not need to annotate every individual path operation with a tag because the router-level tag cascades to all endpoints in that module.

The concrete registrations in api/main.py map routers to tags as follows:

Tag Router Source Lines
auth auth.router L89–L91
config config.router L91–L92
notebooks notebooks.router L92–L93
search search.router L93–L94
models models.router L94–L95
transformations transformations.router L95–L96
notes notes.router L96–L97
embedding embedding.router L97–L98
embeddings embedding_rebuild.router L98–L100
settings settings.router L100–L101
context context.router L101–L102
sources sources.router L102–L103
insights insights.router L103–L104
commands commands_router.router L104–L106
podcasts podcasts.router L106–L108
episode-profiles episode_profiles.router L107–L109
speaker-profiles speaker_profiles.router L108–L110
chat chat.router L109–L111
source-chat source_chat.router L110–L112
credentials credentials.router L111–L113
languages languages.router L112–L114

When you start the application and visit /docs, endpoints appear under collapsible headings such as auth, notebooks, and transformations. This reduces cognitive load and mirrors the project's package structure.

Defining Routes Inside api/routers/

Each router file in api/routers/ exports an APIRouter instance named router. For example, api/routers/auth.py defines authentication-scoped endpoints without worrying about global prefixes or tags:


# api/routers/auth.py

from fastapi import APIRouter, Depends

router = APIRouter()

@router.get("/auth/status")
async def status():
    return {"authenticated": True}

Because api/main.py registers this router with prefix="/api" and tags=["auth"], the effective endpoint is GET /api/auth/status and it appears under the auth group in the documentation.

Extending an Existing Router

You can add new endpoints to a group by modifying the relevant router file. The models router illustrates this pattern:


# api/routers/models.py

from fastapi import APIRouter

router = APIRouter()

@router.get("/models")
async def list_models():
    return {"models": ["gpt-4", "claude"]}

@router.post("/models")
async def create_model(name: str):
    # implementation …

    return {"created": name}

Both methods are automatically prefixed with /api and grouped under the models tag because api/main.py already mounts models.router with those settings.

Testing Endpoints Programmatically

Routes registered through this system behave like standard FastAPI paths in tests. You can exercise them with an async HTTP client:

import httpx
import asyncio

async def test_health():
    async with httpx.AsyncClient(base_url="http://localhost:5055") as client:
        r = await client.get("/health")
        assert r.json() == {"status": "healthy"}

asyncio.run(test_health())

The /health endpoint is defined directly in api/main.py outside the router system, but it follows the same FastAPI conventions as the tagged router endpoints.

Summary

  • api/main.py acts as the single registry for every API router in lfnovo/open-notebook.
  • Routers are imported from the api/routers/ package and attached with app.include_router().
  • A universal prefix="/api" ensures consistent URL paths across all modules.
  • The tags parameter on each include_router() call drives OpenAPI and Swagger UI grouping.
  • Individual router files only need to export an APIRouter instance named router.

Frequently Asked Questions

What is the base prefix for all API routes in open-notebook?

Every router imported into api/main.py is mounted with prefix="/api". Therefore, all endpoints exposed through the router system are reachable under the /api base path.

How do tags affect the Swagger UI documentation?

FastAPI uses the tags argument from app.include_router() to categorize endpoints in the auto-generated /docs interface. In lfnovo/open-notebook, each router receives a descriptive tag such as auth or notebooks, so related endpoints appear together.

Where are individual endpoint functions defined if not in main.py?

Endpoint functions live inside dedicated modules under api/routers/. For instance, authentication logic resides in api/routers/auth.py, while source management lives in api/routers/sources.py. Each module exports a router object that api/main.py consumes.

How can I add a new tag group to the API?

Create a new file in api/routers/ that instantiates an APIRouter, define your endpoints on that router, and then import the module in api/main.py. Add a line such as app.include_router(new_module.router, prefix="/api", tags=["new-tag"]) alongside the existing registrations.

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 →