Skip to course content
Free FastAPI backend course

FastAPI for AI Backend Development

Unit 08.00: Routers, and splitting one file into many

Routers split one file into many without changing any URL.

Prefix and tags declared once

Two routers with their own prefixes, included into one app.

The code registers both and reports the routes.

from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient

invoices = APIRouter(prefix="/invoices", tags=["invoices"])


@invoices.get("/{invoice_id}")
def get_invoice(invoice_id: str) -> dict:
    return {"invoice_id": invoice_id}


accounts = APIRouter(prefix="/accounts", tags=["accounts"])


@accounts.get("/{account_id}")
def get_account(account_id: str) -> dict:
    return {"account_id": account_id}


app = FastAPI()
app.include_router(invoices)
app.include_router(accounts)

client = TestClient(app)
print(client.get("/invoices/INV-1").json())
print(client.get("/accounts/ACC-1").json())
print(f"\nroutes registered: {len([r for r in app.routes if hasattr(r, 'methods')])}")
print(f"tags in the docs : {sorted({t for r in app.routes for t in getattr(r, 'tags', [])})}")

# The prefix is declared once per router rather than repeated on every path,
# and the tag groups the endpoints in the generated documentation.

The prefix is declared once per router rather than repeated on every path, so moving a resource under a new prefix is one edit rather than twenty.

The tag groups those endpoints in the generated documentation, which matters more than it sounds: a forty-endpoint API with no grouping is a flat list nobody can navigate.

The mistake this prevents

The mistake is splitting by technical layer - all the GETs in one file, all the POSTs in another. Split by resource, so everything about invoices is in one place and a change to invoices touches one file.

Takeaway

One router per resource, with the prefix and tag declared once. Split by resource rather than by HTTP method.