Skip to content
Sarj Code Standards

fastapi-class-router-contract

FastAPI HTTP routes use injected *Router.build() owners and explicit named object response models.

Why

Class-owned construction gives dependencies one reviewable composition boundary, while explicit object models keep success envelopes stable and generated clients precise.

Fix

Inject dependencies through *Router.__init__, create and return a local APIRouter from build(), and declare response_model=NamedResponse with a matching named return annotation. Compose the application with FastAPI.include_router(...) instead of declaring HTTP handlers directly on it.

Examples

Before — flagged An application declares an HTTP handler directly
app/routes.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/items", response_model=ItemsResponse)
def items() -> ItemsResponse:
return ItemsResponse(items=[])
After — preferred An application includes a class-owned router
app/routes.py
from fastapi import FastAPI
app = FastAPI()
app.include_router(ItemRouter(service).build())
Before — flagged A module owns an unscoped router
app/routes.py
from fastapi import APIRouter
router = APIRouter()
After — preferred A class builds and returns its router
app/routes.py
from fastapi import APIRouter
class ItemRouter:
def __init__(self, service: Service) -> None:
self._service = service
def build(self) -> APIRouter:
router = APIRouter()
return router