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.
