response_model, response_model_exclude_unset, and Data Serialization
- Last updated
- Prerequisites:
- Pydantic v2 BaseModel
- FastAPI route decorators
- fastapi
- response-model
- serialization
- pydantic
- security
- data-filtering
Read at your depth
The practical view
Use `response_model=MyResponseSchema` in route decorators to filter output: `@app.get('/users/{id}', response_model=UserPublic)`. Even if your function returns a full database entity containing `password_hash`, FastAPI automatically filters the data so only fields declared in `UserPublic` are serialized. Modifiers like `response_model_exclude_unset=True` ensure default values that were not explicitly set are excluded from the JSON output.
The same idea in other frameworks
`@app.get('/users', response_model=UserOut)` or `def get_user() -> UserOut:`.ClassSerializerInterceptor with `@Exclude()` on DTO entities.Jackson `@JsonView` or explicit DTO mapping layers.Manual object mapping (`{ id: u.id, name: u.name }`).Legacy vs modern
Unfiltered Return (Password Leak) vs response_model Filter
Returning raw DB models leaks sensitive password hashes; `response_model` enforces an egress security boundary.
return user_db_object // ❌ Leaks hashed_password and internal flags to public API@app.get('/user/{id}', response_model=UserPublic) // ✅ NEW: Filters private fieldsInterview gotchas
Context
Interviewers test your precision with JSON serialization flags in FastAPI.
The mechanical answer
`response_model_exclude_unset=True` excludes fields that were **not explicitly set** when creating the model instance (it only serializes data explicitly provided in the dictionary/ORM object). `response_model_exclude_defaults=True` excludes any field whose value matches its declared default value, regardless of whether it was set.
Trap
`response_model_exclude_unset` is essential for `PATCH` endpoints where you only want to return fields that were modified.
Context
Checks knowledge of ORM mode migration in Pydantic v2.
The mechanical answer
In Pydantic v2, add `model_config = ConfigDict(from_attributes=True)` to your response schema (which replaces Pydantic v1's `orm_mode = True`). When returning a SQLAlchemy model instance, Pydantic reads attributes via `getattr` instead of dictionary indexing, serializing nested relations cleanly.
Trap
In Pydantic v1 it was `orm_mode = True`; in Pydantic v2 it is `from_attributes = True`.