
Output Models and Response Shaping
Control what you show the world. Learn how to use response_model to filter sensitive data, version responses, and improve API performance.
Output Models and Response Shaping
Security in an API isn't just about who can get in—it's about what you let out. If you query your database for a User, the resulting object likely contains a hashed_password or an internal_admin_note. You must ensure these never reach the client.
In FastAPI, we use Response Models to "shape" the data before it leaves the server.
1. The response_model Parameter
You define your output schema in the path operation decorator. FastAPI then uses this model to filter the data you return.
from pydantic import BaseModel, EmailStr
class UserOut(BaseModel):
username: str
email: EmailStr # Pydantic validates this as a real email format!
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
# This might have 'password', 'secret_key', etc.
user_data = db.fetch_user(user_id)
return user_data
What happens here?
Even if user_data contains 50 fields, FastAPI will only include the fields defined in UserOut in the final JSON response. This is a massive security win.
2. Default Values and exclude_unset
Sometimes your model has fields that are optional. If you don't want to send null values back to the client, you can use response_model_exclude_unset.
@app.get(
"/items/{id}",
response_model=Item,
response_model_exclude_unset=True
)
def read_item(id: int):
return {"id": id, "name": "Tool"} # 'description' is excluded because it wasn't set.
3. Creating Multiple Versions of a Resource
A common pattern in production APIs is to have three versions of the same model:
Base: Common fields likenameandemail.Create: Fields needed for signup (includespassword).Out: Fields safe for the public (includesidandcreated_at).
class UserBase(BaseModel):
email: EmailStr
class UserCreate(UserBase):
password: str
class UserOut(UserBase):
id: int
is_active: bool
4. Performance: Response Models vs. Dicts
FastAPI performs serialization automatically when you return a Pydantic model.
- In Pydantic v2: This is extremely fast (Rust-based).
- Pro Tip: If you are returning a massive list of 10,000 items, returning a raw dictionary can be faster, but you lose the security of the response model filter. We usually recommend sticking with the response model for 99% of use cases.
Visualizing the Filtering Process
graph TD
A["Database Object"] --> B["FastAPI Response Handler"]
B --> C["Check response_model definition"]
C --> D["Filter out extra fields"]
D --> E["Validate remaining fields"]
E --> F["JSON Response (Client)"]
Summary
response_model: The decorator parameter that controls output.- Data Filtering: Never return database objects directly; always filter through a schema.
- Security: This is your last line of defense against data leaks.
- Clarity: Response models automatically document the "Success" response in Swagger.
In the next lesson, we wrap up Module 5 with Exercises to test your Pydantic mastery.
Exercise: The Sensitive Filter
You have a Product model. It has a name, price, and wholesale_cost.
Design a ProductPublic model that hides the wholesale_cost from regular users. How would you apply it to a GET /products endpoint?