Search Topics
Search across all FastAPI topics
Response Model
Core ConceptResponse models control what data your API returns. They filter out sensitive fields, validate output, and generate accurate OpenAPI documentation.
Your /users/me endpoint returns the full user object — including the hashed_password field. A security audit flags it. You accidentally leaked password hashes to every frontend client.
GET /users/me → 200 OK
{
"id": 1,
"username": "alice",
"email": "alice@example.com",
"hashed_password": "$2b$12$LJ3m4ys3Lk.YHxQKfJk0R...",
"is_admin": true,
"created_at": "2024-01-15T10:30:00"
}
# hashed_password is visible to anyone!Question
How does this happen? It's usually innocent — you return your database model directly, and it includes everything. The fix isn't to manually delete fields from a dict. It's to tell FastAPI what's allowed out. That's what response_model does.
How Response Model Filtering Works
Think of response_model as a security filter. Your function can return a full database object with 10 fields, but only the fields in the response model make it to the client. Everything else gets stripped.
DB object
10 fields (incl. password)
Your function
return user
response_model filter
Only 3 fields allowed
Client receives
3 safe fields
The filtering happens automatically. You don't need to manually build a safe dict — just declare the response model and FastAPI does the rest.
Response model concept
What you just learned
response_model acts as a whitelist — only declared fields reach the client
You can safely return full database objects without leaking sensitive data
The filtering is automatic — no manual dict construction needed
Fixing the Password Leak
Your endpoint returns the full user object including hashed_password. How do you create a safe response?
from fastapi import FastAPI
from pydantic import BaseModel
class User(BaseModel):
username: str
password: str
email: str
app = FastAPI()
@app.post("/users")
async def create_user(user: User):
# Returns EVERYTHING, including password!
return userWatch out
This is one of the most common security issues in APIs. It's not just passwords — think about internal IDs, admin flags, cost prices, or API keys. If it's in your database model, it can accidentally leak. Always use separate input and output models.
See the Filter in Action
Watch how response_model strips sensitive fields from your API response. Raw data goes in, only safe fields come out.
Input/output models
What you just learned
Use separate models for input (UserIn) and output (UserOut)
response_model=UserOut strips any fields not in UserOut
You can safely return full DB objects — FastAPI filters them for you
Go Deeper: Fine-Tuning the Response
Sometimes you need more control than just "include these fields." What if you want to exclude fields that weren't explicitly set? Or hide internal fields from a public endpoint?
@app.get(
"/items/{item_id}",
response_model=Item,
response_model_exclude_unset=True,
)
async def read_item(item_id: int):
# Only returns fields that were explicitly set
# If description was never set, it won't appear (even as None)
return items[item_id]
@app.get(
"/items/{item_id}/public",
response_model=Item,
response_model_exclude={"internal_notes", "cost_price"},
)
async def read_item_public(item_id: int):
# These two fields are always stripped, even if set
return items[item_id]Modern Alternative: Return Type Annotations
In newer FastAPI versions, you can skip response_model and use Python's return type annotation instead. Same behavior, cleaner syntax.
# Modern syntax — return type annotation
@app.get("/users/{user_id}")
async def read_user(user_id: int) -> UserOut:
user = get_user(user_id)
return user
# Equivalent to the explicit response_model:
# @app.get("/users/{user_id}", response_model=UserOut)Insight
Both approaches do the same thing. The return type annotation is more Pythonic and gives you better IDE support. Use response_model when you need the extra options like response_model_exclude.
Response control
What you just learned
response_model_exclude_unset hides fields that were never set
response_model_exclude lets you blacklist specific fields
Return type annotations (-> UserOut) are the modern, cleaner alternative
Think about it...
If your response_model has 3 fields but your endpoint returns a dict with 10 fields, what happens to the extra 7?
Hint: Think of response_model as a filter, not a validator.
Key Points
Data Filtering
response_model automatically strips fields not in the model
Security
Prevent leaking passwords, internal IDs, and sensitive data
Return Type
Modern FastAPI supports -> ReturnType as response_model
Exclude Options
Fine-tune with exclude_unset, exclude_defaults, exclude