A Beginner’s Guide to Defining Requests and Responses with FastAPI and Pydantic
📋 Article Highlights (Summary)
- Who Is This For?
Intermediate users who understand the basics of Python and FastAPI but want to learn data validation and type-safe API design - What You’ll Learn
- The core structure of Pydantic models (
BaseModel
) - How to validate incoming request bodies
- Controlling output and generating docs with response models
- Setting default values, aliases, and validation rules on fields
- Examples of nested models, lists, and custom validators
- The core structure of Pydantic models (
- Key Benefits
- Automatically generated validation errors help you build robust, user-friendly APIs
- Model definitions appear in the docs UI, smoothing collaboration with front-end developers
🎯 Target Audience
- Student Engineer A (22)
Has built simple APIs with Flask or Django, now eager to experience type hints and auto-validation - Professional B (30)
Migrating in-house tool APIs to FastAPI and want stricter input checking and output formats - Side-Project Dev C (27)
Wants to safely receive user input in a hobby To-Do app and auto-generate clear API specs
♿ Accessibility Considerations
- Readability: Balanced use of kanji, hiragana, and katakana; simple furigana for key terms
- Structured: Clear hierarchy with headings, subheadings, and bullet lists
- Code Examples: Fixed-width, comment-annotated code blocks for better visibility
- Navigation: “Key Takeaways” at each section’s end for screen reader users
1. Pydantic Models Basics — What Is BaseModel
?
FastAPI adopts Pydantic, a library that leverages Python type hints to automate data validation and serialization. The heart of it is inheriting from BaseModel
, where you only need to specify field types to get validation and defaults out of the box.
from pydantic import BaseModel
class Item(BaseModel):
name: str # Required string
price: float # Required float
is_offer: bool = False # Optional (default: False)
Key Takeaways
- Inherit from
BaseModel
to define your class- Type hints drive validation and serialization
- Fields with default values are treated as optional
2. Applying Models to Request Bodies
2.1 Annotating FastAPI Endpoint Arguments
For endpoints that receive JSON (e.g., POST
or PUT
), simply annotate a function argument with your Pydantic model.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool = False
@app.post("/items/")
async def create_item(item: Item):
# `item` is an instance of Item
return {"item_name": item.name, "item_price": item.price}
- Invalid JSON or wrong types trigger an automatic 422 Unprocessable Entity response.
- The error body details exactly which fields failed validation.
Key Takeaways
- Annotate your endpoint argument with the model to enable validation
- Invalid input automatically returns a detailed error response
3. Controlling Responses with Response Models
3.1 Using the response_model
Parameter
To expose only certain fields in the response, specify a different Pydantic model via response_model
.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ItemIn(BaseModel):
name: str
price: float
class ItemOut(BaseModel):
name: str
price_with_tax: float
@app.post("/items/", response_model=ItemOut)
async def create_item(item: ItemIn):
price_with_tax = item.price * 1.1
return ItemOut(name=item.name, price_with_tax=price_with_tax)
- You can omit internal fields for better security.
- The auto-generated OpenAPI docs reflect
ItemOut
’s structure.
Key Takeaways
- Declare
response_model
to shape your output- Unwanted fields are filtered out automatically
4. Advanced Field Configuration Techniques
4.1 Detailed Settings with Field
Use Pydantic’s Field
function to add constraints and metadata.
from pydantic import BaseModel, Field
class User(BaseModel):
id: int = Field(..., ge=1, description="User ID (integer ≥ 1)")
name: str = Field(..., min_length=1, max_length=50)
email: str = Field(..., regex=r'^\S+@\S+\.\S+$', description="Valid email address")
...
denotes a required field.- Validators like
ge
,le
,min_length
,max_length
,regex
cover many use cases. description
shows up in your OpenAPI docs.
4.2 Aliases and Input Shaping
When JSON keys don’t match your Python naming style, use alias
.
class Product(BaseModel):
product_id: int = Field(..., alias="id")
product_name: str = Field(..., alias="name")
# Client sends {"id":1,"name":"Book"}
# Internally, use product_id and product_name attributes
Key Takeaways
Field
adds constraints and doc annotationsalias
separates JSON keys from property names
5. Nested Models, Lists, and Custom Validators
5.1 Nested Models
Pydantic makes it easy to nest models for complex structures.
class Address(BaseModel):
street: str
city: str
class UserProfile(BaseModel):
name: str
address: Address # Nested model
@app.post("/profiles/")
async def create_profile(profile: UserProfile):
return profile
5.2 List Fields
Accept multiple items by typing a list.
class Tag(BaseModel):
name: str
class Article(BaseModel):
title: str
tags: list[Tag] # List of Tag models
5.3 Custom Validators
For bespoke checks, use the @validator
decorator.
from pydantic import validator
class Order(BaseModel):
quantity: int
@validator("quantity")
def must_be_positive(cls, v):
if v <= 0:
raise ValueError("quantity must be positive")
return v
Key Takeaways
- Nesting and lists simplify complex schemas
@validator
enables custom validation logic
6. Conclusion and Next Steps
This guide covered the essentials of defining request and response schemas with Pydantic in FastAPI:
- Create models by inheriting from
BaseModel
- Apply them to requests and responses with minimal code
- Use
Field
,alias
, andvalidator
for strict, flexible constraints - Model nesting and lists for real-world data shapes
Mastering these patterns lets you leverage FastAPI’s docs and type safety to build rock-solid APIs. Next up, combine these skills with database integration and authentication for fully featured services!