06 Request Body Pydantic
title: Pydantic Request Bodies in FastAPI REST APIs weight: 16 date: 2026-06-28 lastmod: 2026-06-28 description: Master Pydantic models for FastAPI request bodies with field validation, nested models, custom validators, and configuration for automatic request parsing and validation. tags: [api-development, fastapi]
Pydantic models define request body schemas in FastAPI using Python type hints with field validation, default values, nested models, custom validators, and automatic JSON parsing, generating OpenAPI schemas automatically.
```mermaid
flowchart TD
A[Pydantic Model] --> B[Field Types]
A --> C[Validation]
A --> D[Nested Models]
B --> E[str, int, float, bool]
B --> F[EmailStr, HttpUrl]
C --> G[field_validator]
C --> H[Field constraints]
D --> I[Model as field type]
style A fill:#e1f5fe
style B fill:#c8e6c9
style C fill:#fff9c4
Define a Pydantic model class inheriting from BaseModel with type-annotated fields. FastAPI receives JSON data, validates it against the model, and injects the validated instance into the route handler. Invalid data returns 422 with field-level error details.
Think of Pydantic models like a tax form. The form has specific fields (name, income), types (text, number), and rules (income must be positive). When you submit the form (request body), an inspector (Pydantic) checks everything before processing.
Example: Basic Pydantic Models
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from typing import Optional, List
from datetime import datetime
app = FastAPI()
class UserCreate(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
email: EmailStr
age: int = Field(ge=0, le=150)
password: str = Field(..., min_length=8)
role: str = Field(default="user", pattern="^(user|admin)$")
tags: List[str] = []
class UserResponse(BaseModel):
id: int
name: str
email: str
role: str
created_at: datetime
model_config = {"from_attributes": True}
@app.post("/api/users", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
"""Create user with Pydantic validation."""
# user is validated UserCreate instance
# Return a dict that matches UserResponse
return {
"id": 1,
"name": user.name,
"email": user.email,
"role": user.role,
"created_at": datetime.now()
}
Example: Nested Models and Validation
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
class Address(BaseModel):
street: str = Field(..., min_length=5)
city: str = Field(..., min_length=2)
zip_code: str = Field(..., pattern="^[0-9]{5}(-[0-9]{4})?$")
country: str = Field(default="USA")
class OrderItem(BaseModel):
product_id: int = Field(..., ge=1)
quantity: int = Field(..., ge=1, le=100)
unit_price: float = Field(..., gt=0)
@field_validator("unit_price")
@classmethod
def check_price(cls, v):
if v > 100000:
raise ValueError("Unit price cannot exceed 100000")
return v
class OrderCreate(BaseModel):
customer_id: int = Field(..., ge=1)
items: List[OrderItem] = Field(..., min_length=1)
shipping_address: Address
notes: Optional[str] = Field(None, max_length=500)
@app.post("/api/orders", status_code=201)
def create_order(order: OrderCreate):
total = sum(item.quantity * item.unit_price for item in order.items)
return {
"order_id": 123,
"total": total,
"items_count": len(order.items),
"shipping": order.shipping_address.model_dump()
}
Example: Multiple Body Parameters
from fastapi import FastAPI, Body
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class User(BaseModel):
username: str
email: str
@app.post("/api/orders")
def create_order_with_user(
item: Item,
user: User,
priority: int = Body(ge=1, le=5),
coupon: str = Body(None, max_length=20)
):
return {
"item": item.model_dump(),
"user": user.model_dump(),
"priority": priority,
"coupon": coupon
}
Common Mistakes
- Not using Field validation — Without Field(min_length, max_length, pattern), you lose validation power. Always add constraints to fields.
- Missing model_config for ORM mode — When returning SQLAlchemy models, set model_config = {"from_attributes": True} to enable attribute access.
- Putting validation in the route handler — Define validation in Pydantic models using field_validator, not in the handler function.
- Not using EmailStr for emails — Import EmailStr from pydantic. It validates email format automatically. Using str misses email validation.
- Forgetting to use response_model — Without response_model, FastAPI returns all fields including sensitive ones. Define response models that exclude passwords.
Practice Questions
- What is the difference between Field and field_validator?
- How do you create nested Pydantic models?
- What does response_model do in a route decorator?
- How do you make a field optional with a default value?
- Challenge: Design Pydantic models for a hotel booking system. Include models for Guest, Booking (with check-in/out dates), Payment (amount, method, card_last_four), and Room (type, rate, amenities). Add appropriate validation for each field.
FAQ
Mini Project
Design a complete set of Pydantic models for a social media API. Include models for: User (register, profile, public), Post (create, response, with author details), Comment (create, response with nesting), and Feed (paginated response with posts). Add validation for emails, URLs, text lengths, and custom validators for password strength and username format.
What's Next
Now learn about response models in Building REST APIs with FastAPI.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro