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:
1from datetime import datetime2from pydantic import BaseModel, Field, ValidationError34class IngestRequest(BaseModel):5 doc_id: int6 chunks: list[str] = Field(min_length=1, max_length=64)7 chunk_size: int = Field(500, ge=100, le=2000)8 uploaded_at: datetime910req = IngestRequest.model_validate(11 {"doc_id": "42", "chunks": ["Refund policy..."], "uploaded_at": "2026-09-01T10:30:00"})12print(repr(req.doc_id), repr(req.uploaded_at))13print(req.model_dump_json())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:
('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 shortThe three jobs inside FastAPI
- 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.
- Serialise the response — your return value is checked against the return type and turned into JSON.
datetime,UUID,Enumand nested models just work. - 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 thedetaillist. - "What is the difference between Pydantic v1 and v2?" — v2 has a Rust core and new method names:
model_dumpinstead ofdict,field_validatorinstead ofvalidator, andmodel_configinstead of an innerConfigclass. - "Is validation slow?" — Usually well under a millisecond for a normal request, which is tiny next to a model call.