Under The Hood
backend fastapi FastAPI 0.100+ / Pydantic v2

FastAPI: the Pydantic validation pipeline

Last updated
Prerequisites:
FastAPI basics
Familiarity with Python type annotations
  • fastapi
  • pydantic
  • validation
  • pydantic-core
  • rust
  • serialization

Read at your depth

The practical view

FastAPI uses Pydantic models for request bodies, query params, and response schemas: class Item(BaseModel): name: str; price: float. On each request, FastAPI parses the JSON body, validates it against the model, coerces types (e.g., '123' → 123 for int fields, ISO strings → datetime), and injects a validated instance into your handler: async def create(item: Item). Invalid data produces a 422 response with field-level errors automatically. The same models generate OpenAPI schemas for interactive docs. Declare the return type in the handler signature (-> Item) and FastAPI serializes/validates the response too.

Legacy vs modern

Manual dict validation vs typed Pydantic boundary

Hand-rolled dict checks are scattered and incomplete; a Pydantic model centralizes coercion, validation, and OpenAPI generation at the boundary.

before → after
Manual validation
data = request.json()
if "name" not in data or not isinstance(data["name"], str):  # ❌ OLD: manual isinstance checks scattered per handler
    return 400 "bad name"
price = float(data.get("price", 0))
Pydantic-typed boundary
class Order(BaseModel):  # ✅ NEW: typed Pydantic boundary validates + coerces at the edge
    user_id: int
    items: list[OrderItem]

@app.post("/orders", response_model=OrderResult)
def create_order(order: Order): ...

Interview gotchas

Context

The interviewer wants the pipeline order — routing, dependency resolution, body parsing, validation — and the failure points (422 vs 500).

The mechanical answer

Starlette matches the route and invokes FastAPI's endpoint wrapper. FastAPI resolves the endpoint's dependency graph (including dependencies with their own request-body parameters), then parses the request body according to the declared body type. The body is validated through the compiled Pydantic validator; failures are normalized to ValidationError and returned as a 422 with structured detail. Only after validation passes does your handler run. The response path mirrors it: the return value is validated against the response_model and serialized (with exclude_none/filters applied). Errors inside the handler propagate to the exception handler (usually 500), distinct from the boundary 422.

Trap

The naive answer is 'FastAPI just calls your function'. The order of body parsing → validation → handler → response validation matters, and knowing that dependencies run first (with their own validations) explains ordering bugs. Another trap: '422 only comes from JSON bodies' — query/header/path params declared with types also produce 422s through the same pipeline. Mentioning response_model validation as a separate boundary (not just request) is a strong differentiator.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.