Architecture
How a request actually moves from the ASGI server through routing, dependency resolution, and validation.
Third-party documentation. This is independently authored analysis of the public FastAPI codebase — not the official docs, and not reviewed or endorsed by the FastAPI team.
Architecture
FastAPI does not reimplement HTTP handling or data validation — it composes two libraries that already solve those problems well, and adds a thin, opinionated layer connecting them: route introspection, a dependency-injection graph, and automatic schema generation.
Request lifecycle
- An ASGI server (Uvicorn/Hypercorn) accepts the connection and calls into Starlette's router.
- Starlette matches the path and method to a registered
APIRoute. - FastAPI's
request_response()adapter wraps the plain Python function into an ASGI-compatible callable. solve_dependencies()walks the route's dependency graph — built once, at startup, viainspectandtyping.get_type_hints()— and resolves everyDepends(...)in topological order, reusing sub-dependencies that appear more than once in the same request.- Path, query, header, cookie, and body parameters are validated and coerced by Pydantic; a failure raises
RequestValidationErrorand short-circuits to a 422 response before the endpoint ever runs. - The endpoint function executes.
- The return value is validated against the declared
response_model(if any) and serialized back to JSON. - Any generator-based dependencies (
yield) run their teardown code inside anAsyncExitStack, guaranteed to execute even if the endpoint raised.
Design principle: introspection happens once
The expensive part — inspecting a function's signature to determine what's a path parameter, query parameter, header, or dependency — happens exactly once, when the route is registered at startup, not on every request. The result is cached as a Dependant object attached to the route. This is why FastAPI's per-request overhead stays close to Starlette's despite the amount of declarative behavior layered on top.
