Response Models and Data Validation¶
🎯 What You’ll Learn¶
- How to define response schemas using Pydantic
- How FastAPI validates and filters response data
- How to use response models to enforce structure and hide sensitive fields
- How this improves documentation and client integration
📦 What Is a Response Model?¶
A response model is a Pydantic class that defines the structure of the data your API returns to clients. FastAPI uses it to:
- Validate your return values
- Filter out unexpected or sensitive fields
- Automatically generate response schemas in the docs
This is especially useful when:
- You want to hide internal fields (e.g. passwords, database IDs)
- You want to guarantee consistent output for frontend or API consumers
🛠️ Step-by-Step: Defining a Response Model¶
Let’s say we have a Task model with extra internal fields we don’t want to expose.
📄 main.py¶
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# Internal model (e.g. from database)
class Task(BaseModel):
id: int
title: str
completed: bool
internal_notes: str
# Public response model
class TaskResponse(BaseModel):
title: str
completed: bool
@app.get("/task", response_model=TaskResponse)
def get_task():
task = Task(
id=1,
title="Buy milk",
completed=False,
internal_notes="Urgent task"
)
return task
🔍 What’s Happening Here?¶
Taskis your full internal model.TaskResponseis the public-facing version.response_model=TaskResponsetells FastAPI to:- Validate the returned data
- Filter out fields not in
TaskResponse(likeidandinternal_notes) - Generate clean documentation
🧪 Try:
-
Visit
/task→ You’ll get:{ "title": "Buy milk", "completed": false } -
Visit
/docs→ You’ll see the response schema matchesTaskResponse
🔐 Why This Matters¶
- Security: Prevent leaking sensitive fields (e.g. passwords, tokens)
- Consistency: Frontend always gets the same shape of data
- Documentation: Clients know exactly what to expect
🧪 Example: Filtering Sensitive User Data¶
class User(BaseModel):
id: int
username: str
email: str
password: str # sensitive
class PublicUser(BaseModel):
username: str
email: str
@app.get("/user", response_model=PublicUser)
def get_user():
user = User(
id=1,
username="enrico",
email="enrico@example.com",
password="supersecret"
)
return user
🧭 Try:
/user→ ✅ Only returnsusernameandemail/docs→ Shows only the public fields
🧠 Optional: Response Model Configuration¶
You can customize response behavior using Pydantic’s Config class:
class TaskResponse(BaseModel):
title: str
completed: bool
class Config:
orm_mode = True
orm_mode = Trueallows FastAPI to work with ORM objects (e.g. SQLAlchemy) that aren’t plain dicts.- This becomes essential when you connect to a database later.
🧠 Practice Challenge¶
Create a User model with:
id,username,email,password,is_admin
Then:
- Create a
PublicUserresponse model that excludespasswordandis_admin - Return a full
Userobject from an endpoint, but useresponse_model=PublicUser - Test it in
/docsand verify the output is filtered