FastAPI Essentials

Course Content

FastAPI Essentials

1 sections · 32 lessons

Explain the concept of routers in FastAPI.


Prefixes stack, dependencies inheritapp/v1 + ragrouter (API key)healthrouter (no auth)/v1/rag/query/v1/rag/documents/healthz
One dependency on the RAG router protects every route in it, while probes reach the health router without credentials.

What you need to know

A router works like a small app: you register routes on it with the same decorators, then include it in the real app. Settings on the router apply to all its routes, and include_router can add more.

Python
from typing import Annotatedfrom fastapi import APIRouter, Depends, FastAPI, Header, HTTPExceptiondef verify_api_key(x_api_key: Annotated[str | None, Header()] = None):    if x_api_key != "k-123":        raise HTTPException(401, "Bad API key")# app/api/rag.pyrag = APIRouter(prefix="/rag", tags=["rag"], dependencies=[Depends(verify_api_key)])@rag.post("/query")def query(q: str):    return {"answer": f"Top passages for {q!r}"}@rag.post("/documents", status_code=202)def add_document(url: str):    return {"queued": url}# app/api/health.pyhealth = APIRouter(tags=["ops"])@health.get("/healthz")def healthz():    return {"ok": True}# app/main.pyapp = FastAPI()app.include_router(rag, prefix="/v1")app.include_router(health)

Real results:

Text
paths in the app          -> ['/healthz', '/v1/rag/documents', '/v1/rag/query']POST /v1/rag/query        -> 401            (no key: the router's dependency ran)  with X-Api-Key: k-123   -> {'answer': "Top passages for 'refund'"}GET  /healthz             -> {'ok': True}   (no key needed)tags in /docs             -> ['ops', 'rag']

The prefixes stack: /v1 from include_router plus /rag from the router plus /query from the route. The API-key check was written once but protects both RAG routes. Tags group the endpoints in /docs.

A typical layout

Text
app/  main.py          # creates app, lifespan, middleware, include_router calls  api/    chat.py        # router: /chat, streaming endpoints    rag.py         # router: /rag, protected by API key    admin.py       # router: /admin, protected by admin role    health.py      # router: /healthz, /readyz, no auth  deps.py          # shared dependencies: get_db, current_user, get_model  schemas.py       # Pydantic models

Routers can include other routers, so /v1 can be one router that includes chat, rag and admin.

A real-life example

An ed-tech company's AI tutor started as one main.py with 45 routes. Two teams edited it daily and merge conflicts were constant. A new admin endpoint for deleting student data was added without the admin check, because it was copied from a public route.

They split it into five routers. The admin router has dependencies=[Depends(require_role("admin"))], so any route added to that file is protected automatically. When they needed a breaking change to the chat response format, they created a v2 chat router alongside v1, and moved the mobile app over gradually while old app versions kept working. The health router stayed outside all auth so Kubernetes probes never needed a key.

Follow-up questions to expect

  • "Does route order still matter in a router?" — Yes. Within a router, fixed paths such as /models/latest must come before /models/{model_id}.
  • "What is the difference between a router and a mounted sub-app?" — A router merges into the main app and shares its docs, middleware and exception handlers. app.mount("/legacy", other_app) attaches a separate ASGI app with its own docs and middleware.
  • "How do you add a dependency to only some routes of a router?" — Put it on those routes with dependencies=[...] on the decorator, or split them into a second router.