← Docs
FastAPI Patterns
Production-grade recipes for dependency injection, middleware, and structured responses.
Dependency Injection
Use Depends() to inject database sessions, auth contexts, and configuration. Keep route handlers thin — business logic lives in service layers, never in path operation functions.
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
async def get_db():
async with async_session() as session:
yield session
@router.get("/items")
async def list_items(db: AsyncSession = Depends(get_db)):
return await item_service.get_all(db)Middleware Stack
Chain CORS, timing, and request-ID middleware via app.add_middleware(). Order matters — outermost middleware runs first on ingress and last on egress.
from starlette.middleware.base import BaseHTTPMiddleware
import time, uuid
class TimingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
request.state.request_id = str(uuid.uuid4())
start = time.perf_counter()
response = await call_next(request)
response.headers["X-Request-ID"] = request.state.request_id
response.headers["X-Response-Time"] = str(time.perf_counter() - start)
return responseStructured Responses
Define Pydantic models for every response shape. Use response_model to enforce serialization contracts and auto-generate OpenAPI schemas.
from pydantic import BaseModel
from typing import Generic, TypeVar
T = TypeVar("T")
class APIResponse(BaseModel, Generic[T]):
success: bool = True
data: T | None = None
error: str | None = None
@router.get("/users/me", response_model=APIResponse[UserOut])
async def get_current_user(current_user: User = Depends(get_current_user)):
return APIResponse(data=UserOut.model_validate(current_user))Error Handling
Register exception handlers globally. Raise typed HTTP exceptions in service layers and let the handler normalize the response envelope.
from fastapi import Request
from fastapi.responses import JSONResponse
class AppException(Exception):
def __init__(self, status: int, detail: str):
self.status = status
self.detail = detail
@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
return JSONResponse(
status_code=exc.status,
content={"success": False, "error": exc.detail}
)