1. Path operations, parameters & Pydantic models
Start with the contract: make inputs, outputs and failure behavior explicit before adding infrastructure. This keeps the feature easy to reason about and gives tests a stable boundary.
2. Reusable dependencies with Depends()
Apply the pattern through a small vertical slice. Keep framework wiring at the edge and business decisions in focused functions or services that can be tested without starting the whole application.
from typing import Annotated
from fastapi import Depends, FastAPI, Query
app = FastAPI()
def pagination(limit: Annotated[int, Query(ge=1, le=100)] = 20):
return {"limit": limit}
@app.get("/tasks")
def list_tasks(page: Annotated[dict, Depends(pagination)]):
return {"items": [], **page}3. OpenAPI, Swagger UI & ReDoc
Treat failure paths as part of the design. Add bounded resource usage, meaningful errors and a verification step so the behavior remains dependable under real production conditions.
4. Hands-on Exercise
Build the feature
Build an in-memory books API with typed path and query parameters, a reusable pagination dependency, and useful OpenAPI descriptions.
Definition of done
- The happy path works through the real HTTP boundary.
- At least one failure path is handled and tested.
- Configuration and secrets stay outside source code.
- The README explains how to run and verify the result.
5. Knowledge Check
Why is Depends() better than calling a shared helper directly?
Show answer
FastAPI can resolve, cache, override and document the dependency graph, which also makes the boundary straightforward to test.