Get AI summaries of any video or article — Sign up free
Python FastAPI Tutorial (Part 4): Pydantic Schemas - Request and Response Validation thumbnail

Python FastAPI Tutorial (Part 4): Pydantic Schemas - Request and Response Validation

Corey Schafer·
5 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Create a dedicated schemas.py file to define Pydantic models that act as the API contract for both requests and responses.

Briefing

FastAPI’s request/response validation becomes concrete once Pydantic schemas define the API contract—what clients must send and what the server will return. By adding three Pydantic models (shared fields, create payload, and response payload) and wiring them into endpoint decorators, the API gains runtime enforcement of field constraints, automatic 422 error responses for bad input, and documentation that lists exact field types and validation rules.

The tutorial starts by addressing a gap in the existing endpoints: they return plain dictionaries with no schema, so the interactive docs don’t show response structure, and there’s no safe way to create posts through the API. Pydantic is introduced as the validation layer built into FastAPI (installed automatically as a dependency). Unlike “type hints as documentation only,” Pydantic uses those type hints at runtime to validate incoming data and generate detailed error messages when data doesn’t match.

A new schemas.py file is created to centralize the contract. A PostBase model inherits from BaseModel and defines shared fields for title, content, and author. Field constraints are added using Field—title requires a minimum length of 1 and a maximum length of 100 characters; content requires at least 1 character; author requires at least 1 and at most 50 characters. Separate models then clarify intent: PostCreate inherits from PostBase to represent what clients send when creating a post, while PostResponse inherits from PostBase and adds server-generated fields like id (integer) and date_posted (string for now). PostResponse also sets model configuration with config dict using from_attributes=True, which prepares the schemas to read data from attribute-based objects (important once a database layer replaces in-memory dictionaries).

With schemas in place, main.py is updated to import PostCreate and PostResponse. The GET endpoints gain response_model annotations: the “list posts” route returns a list of PostResponse objects, and the “single post” route returns one PostResponse. These response models do more than improve docs—they validate outgoing data and filter out extra fields, helping prevent accidental data leakage.

A new POST /posts endpoint is added with response_model=PostResponse and status_code=201. FastAPI uses the PostCreate type hint to parse and validate the JSON body before the endpoint function runs. If validation fails, the client receives a 422 response with specific details. When validation succeeds, the handler generates an id, constructs a post dictionary from the validated fields, appends it to the in-memory list, and returns the new post.

Testing in the interactive docs confirms the behavior: successful creation returns a 201 with id and date_posted; missing required fields triggers 422 errors; and constraint violations (like too-short strings) produce precise messages. Finally, the tutorial highlights a practical limitation: created posts vanish after a server restart because they’re stored in a Python list in memory. Persistent storage requires a database, which is slated for the next tutorial.

Overall, the key takeaway is that Pydantic schemas define the API contract and enable FastAPI to handle validation, serialization, and documentation consistently—before introducing database-backed persistence and relationships.

Cornell Notes

Pydantic schemas turn FastAPI endpoints into a strict contract: clients know exactly what to send, and the server guarantees what it returns. The tutorial builds three models—PostBase (shared fields with constraints), PostCreate (what clients submit), and PostResponse (what the API returns, including server-generated id and date_posted). By attaching response_model to GET routes and using PostCreate as the request body type for POST, FastAPI validates inputs and outputs at runtime and returns detailed 422 errors when data is invalid. The interactive docs automatically display field types and constraints, making the API self-describing. The in-memory list used for posts is only temporary, so data disappears after restart, motivating the next step: adding a database.

How do PostBase, PostCreate, and PostResponse differ, and why split them into separate classes?

PostBase holds shared fields for title, content, and author, including constraints via Field (e.g., title min length 1 and max length 100; author min length 1 and max length 50). PostCreate inherits from PostBase and represents the payload clients send when creating a post; it currently matches PostBase but leaves room for future changes (like removing author if authentication supplies it). PostResponse also inherits from PostBase but adds fields the client doesn’t provide—id (integer) and date_posted (string at this stage). This separation clarifies intent and keeps request and response shapes aligned with real API behavior.

What does response_model change for GET endpoints beyond documentation?

Adding response_model tells FastAPI to validate and serialize the data returned by the endpoint against the schema. For the “list posts” route, response_model is a list of PostResponse objects; for the “single post” route, it’s a single PostResponse. If returned data is missing required fields or includes extra fields not in the schema, FastAPI enforces the schema—missing fields trigger errors, and extra fields are filtered out—reducing the risk of exposing unintended data.

How does FastAPI produce a 422 validation error before endpoint code runs?

The POST endpoint uses a type hint of PostCreate for the request body. FastAPI parses the incoming JSON and validates it against PostCreate’s field types and constraints. If the payload violates rules—such as omitting a required field like author or providing a string that fails min/max length—FastAPI returns a 422 response containing details about what failed. The endpoint function only runs after the payload passes validation.

Why set from_attributes=True in the PostResponse model configuration?

from_attributes=True configures Pydantic v2 models to read data from objects with attributes (dot notation) rather than only dictionaries (bracket notation). Right now, posts are stored as dictionaries in memory, so the API works either way. Once a database is introduced, posts will likely be represented by ORM objects accessed via attributes (e.g., post.title). Without from_attributes=True, Pydantic could fail to serialize those objects into the response schema.

Why do newly created posts disappear after restarting the server?

Created posts are appended to a Python list held in memory. When the development server restarts, that list is recreated from the hard-coded initial data, so any posts created through the API are lost. Persistent behavior requires replacing the in-memory list with a database-backed storage layer.

Review Questions

  1. What specific constraints are applied to title and author in PostBase, and how do those constraints surface to clients when validation fails?
  2. How do response_model annotations on GET routes affect both the interactive docs and the actual runtime behavior of returned data?
  3. What role does from_attributes=True play, and what change in the data layer makes it necessary?

Key Points

  1. 1

    Create a dedicated schemas.py file to define Pydantic models that act as the API contract for both requests and responses.

  2. 2

    Use Field constraints (min_length/max_length) in PostBase so invalid inputs trigger automatic 422 errors with detailed messages.

  3. 3

    Separate PostCreate (client input) from PostResponse (server output) to keep request/response shapes clear and extensible.

  4. 4

    Attach response_model to GET endpoints so FastAPI validates outgoing data, filters extra fields, and generates precise schema documentation.

  5. 5

    Use PostCreate as the request body type for POST endpoints so FastAPI validates JSON before endpoint logic runs.

  6. 6

    Set from_attributes=True in response models to support attribute-based ORM objects once a database replaces in-memory dictionaries.

  7. 7

    Recognize that in-memory storage loses data on restart, so persistence requires a database in the next step.

Highlights

Pydantic schemas make FastAPI’s interactive docs reflect real validation rules—types, required fields, and min/max length constraints.
FastAPI returns a 422 validation error automatically when request JSON fails schema checks, without manual if-statements or try/except blocks.
response_model doesn’t just document responses; it also validates and filters returned data to match the schema.
from_attributes=True prepares schemas for the ORM-style dot-notation access pattern that comes with database integration.
Because posts are stored in a Python list in memory, created posts vanish after server restart—persistence needs a database.

Topics

  • Pydantic Schemas
  • Request Validation
  • Response Validation
  • FastAPI response_model
  • Database Preparation

Mentioned