FastAPI Essentials

Course Content

FastAPI Essentials

1 sections · 32 lessons

What is the role of Pydantic in FastAPI?


What you need to know

Take a RAG ingestion request:

Python
from datetime import datetimefrom pydantic import BaseModel, Field, ValidationErrorclass IngestRequest(BaseModel):    doc_id: int    chunks: list[str] = Field(min_length=1, max_length=64)    chunk_size: int = Field(500, ge=100, le=2000)    uploaded_at: datetimereq = IngestRequest.model_validate(    {"doc_id": "42", "chunks": ["Refund policy..."], "uploaded_at": "2026-09-01T10:30:00"})print(repr(req.doc_id), repr(req.uploaded_at))print(req.model_dump_json())
Text
42 datetime.datetime(2026, 9, 1, 10, 30){"doc_id":42,"chunks":["Refund policy..."],"chunk_size":500,"uploaded_at":"2026-09-01T10:30:00"}

And bad input, all problems reported at once:

Text
('doc_id',)      Input should be a valid integer, unable to parse string as an integer('chunks',)      List should have at least 1 item after validation, not 0('chunk_size',)  Input should be greater than or equal to 100('uploaded_at',) Input should be a valid datetime or date, input is too short

The three jobs inside FastAPI

  1. Validate the request — FastAPI builds a Pydantic model from the path, query, headers and body. If it fails, your handler never runs and the client gets a 422 listing each bad field.
  2. Serialise the response — your return value is checked against the return type and turned into JSON. datetime, UUID, Enum and nested models just work.
  3. Describe the API — each model produces a JSON Schema ({'minimum': 100, 'maximum': 2000, 'type': 'integer', ...}), and FastAPI assembles these into /openapi.json.

Lax versus strict

By default Pydantic is lax: it converts values when the meaning is clear, such as "42" to 42. That suits query strings, which are always text. If you want exact types in a JSON body, set model_config = ConfigDict(strict=True), and "5" for an int is then rejected. Extra, unknown fields are ignored by default; ConfigDict(extra="forbid") rejects them, which catches typos such as temprature.

A real-life example

A legal-tech company lets customers upload contracts for a RAG search tool. The upload client sometimes sends chunk_size: 50, which creates thousands of tiny chunks and multiplies the embedding bill. Before Pydantic rules, this was found on the monthly invoice.

With chunk_size: int = Field(500, ge=100, le=2000), the bad request fails in about a millisecond with a message naming the field, before any embedding call. The same model is reused in the worker that reads jobs from the queue, so the rule lives in one place. The team also uses Pydantic on the way out of the LLM: Answer.model_validate_json(llm_reply) checks that the model's JSON has the fields the app expects.

Follow-up questions to expect

  • "Who turns a ValidationError into a 422?" — FastAPI. It catches the error, raises RequestValidationError, and its default handler returns the detail list.
  • "What is the difference between Pydantic v1 and v2?" — v2 has a Rust core and new method names: model_dump instead of dict, field_validator instead of validator, and model_config instead of an inner Config class.
  • "Is validation slow?" — Usually well under a millisecond for a normal request, which is tiny next to a model call.