FastAPI
An ASGI framework whose one idea is that the type hints are the contract. Validation, serialisation, dependency injection and the OpenAPI schema are all derived from the signature, which is why a FastAPI handler is short and why a sloppy signature produces a sloppy public API.
What it is built on
| Layer | Does |
|---|---|
| Starlette | ASGI, routing, middleware, WebSockets, TestClient |
| Pydantic | validation, serialisation, the JSON Schema |
| FastAPI | dependency injection, OpenAPI, the glue |
Worth knowing, because half of “FastAPI” questions are really Starlette
questions — middleware, background tasks and Request all come from there —
and the other half are Pydantic questions.
The shape of a handler
@app.post("/orders", response_model=OrderOut, status_code=201)
async def create(
body: OrderIn,
session: Annotated[AsyncSession, Depends(get_session)],
user: Annotated[User, Depends(current_user)],
) -> OrderOut:
order = await service.create(session, user, body)
return orderFour things happened without you writing them: the body was parsed and
validated into OrderIn, the session and user were constructed and injected,
the return value was validated against OrderOut, and the whole thing appeared
in /docs with a schema. That is the entire pitch.
Where each parameter comes from is inferred, which is the rule to state:
| Parameter | Source |
|---|---|
| Named in the path | path parameter |
| A Pydantic model | request body |
| A scalar not in the path | query parameter |
Depends(...) |
dependency injection |
Request, Response |
the raw Starlette objects |
Why async, and the trap
FastAPI runs on an event loop, so a def handler goes to a threadpool and an
async def handler runs on the loop. Blocking inside async def stalls every
concurrent request, which is the single most common FastAPI performance bug
and has its own note: FastAPI and the event loop.
Lifespan, not startup events
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.http = httpx.AsyncClient(timeout=5)
yield
await app.state.http.aclose()
app = FastAPI(lifespan=lifespan)@app.on_event("startup") is deprecated. Lifespan is one function with the
teardown next to the setup, which is what makes it hard to leak a connection
pool. Everything expensive and long-lived is built here — see
Writing a client for an external API.
Where it fits against the alternatives
| Reach for | |
|---|---|
| JSON API, async I/O, typed | FastAPI |
| Admin, ORM, auth, templates included | Django + DRF |
| Small, sync, minimal | Flask |
| Layered DI, msgspec, one codebase | Litestar |
The honest comparison with DRF is not performance — it is that Django brings a batteries-included ecosystem you either need or carry, while FastAPI brings a type-driven core you assemble around. See Django vs Django REST Framework (DRF) Guide and Litestar.
This folder
Interview angle 5
- “What is FastAPI built on?” - Starlette for ASGI, routing, middleware and WebSockets; Pydantic for validation and serialisation. FastAPI adds dependency injection and OpenAPI generation. Half of “FastAPI” questions are really Starlette or Pydantic questions.
- “How does it know where a parameter comes from?” - by inference from the signature: named in the path is a path parameter, a Pydantic model is the body, a bare scalar is a query parameter, and
Dependsis injection. The type hints are the contract, which is the whole design. - “Why is FastAPI fast?” - it is ASGI, so an
async defhandler that awaits I/O frees the worker to serve another request. Not because Python got faster — and the benefit disappears the moment you block insideasync def. - “How do you manage a connection pool across requests?” - a
lifespancontext manager: build it beforeyield, close it after.@app.on_event("startup")is deprecated, and lifespan keeps the teardown next to the setup so it is hard to leak. - “FastAPI or Django REST Framework?” - DRF when you want the Django ecosystem: admin, ORM, auth, migrations, templates. FastAPI when the service is a typed JSON API over async I/O and you would rather assemble the pieces than carry the ones you do not use.