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.
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))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.
Context
This checks current knowledge — v2's Rust core changed performance characteristics and some model behaviors; the interviewer wants the mechanics.
The mechanical answer
Pydantic v2 moved validation and serialization into pydantic-core, a Rust library compiled to a native extension. Each model compiles its schema (CoreSchema) into a validator that runs in Rust — type checks, coercion, constraint checks, and error collection happen in one native pass instead of per-field Python calls. Model definitions remain Python classes; the schema is compiled lazily on first use and cached. Behavioral changes vs v1: strict modes (str types no longer coerced from ints by default in strict), union handling differences, and the new model_validate/model_dump APIs. The performance impact: validation is typically an order of magnitude faster, so validating large request/response payloads on hot paths is no longer a bottleneck, but deep nested models still cost proportional to size.
Trap
The trap answer is 'FastAPI is fast because it uses Rust'. FastAPI itself is Python/Starlette; Pydantic v2's validator core is the Rust component. Another trap: 'v2 is a drop-in for v1' — some coercions and config options changed (smart union mode, error message formats), which surprises migrating teams. Candidates who mention CoreSchema compilation and the model_validate API prove they have actually used v2 rather than read a blog post.