Hello! Welcome back to your FastAPI course.
In our last lesson, we focused on model_config, particularly the from_attributes=True setting. You learned how this configuration is the key to letting Pydantic schemas read data directly from ORM objects, a concept directly analogous to how Django REST Framework's ModelSerializer works with Django models.
Today, we build directly on that foundation. We'll explore how FastAPI uses Pydantic schemas to shape the data you send back to the client. This lesson addresses the learning outcome: Use the response_model parameter to filter and validate response data, comparing it to DRF serializer-based responses.
You'll learn how to ensure your API only exposes the data it should—a critical aspect of security and API design—and see how this is a more explicit and type-safe approach compared to some patterns in DRF.
1. The Core Task: Shaping Your API's Output
In any API, the data you store in your database is often not what you want to expose to the outside world. You might have internal fields, sensitive information like password hashes, or simply data that isn't relevant to a particular endpoint.
In Django REST Framework, you solve this by defining a Serializer. You meticulously list the fields to include or exclude to control the final JSON output.
FastAPI accomplishes the same goal using the response_model parameter in the path operation decorator.
FastAPI Path Operations for Django Developers
The article 'FastAPI Path Operations for Django Developers' from OddBird provides a direct comparison. This specific section succinctly describes how response_model handles output.
Please read the single bullet point under the heading 'FastAPI Path Operations' that begins with 'Automatic serialization of the response body...'. This gives you a concise, high-level summary of what response_model does.
As the article notes, FastAPI uses the Pydantic model you provide to response_model to guarantee the structure and types of the response body. Your function can return a dictionary, an ORM object, or another Pydantic model, and FastAPI will handle the conversion and filtering.
2. The Primary Use Case: Filtering Sensitive Data
Let's tackle the most important reason to use a response_model: security. You never want to accidentally leak sensitive data.
Imagine you have a user model that stores a password field. Your API needs this field for user creation (POST /users/), but it must never be included in any response (GET /users/1).
In DRF, you might create two serializers: UserCreateSerializer (which includes password) and UserSerializer (which excludes it). FastAPI uses the same pattern, but with Pydantic models.
Response Model - Return Type - FastAPI
The official FastAPI documentation has a perfect example demonstrating this exact scenario. It shows how to use separate input and output models to prevent a password from being returned.
Read the sections 'Return the same input data' and 'Add an output model'. Pay close attention to the definition of UserIn and UserOut, and how response_model=UserOut is used in the @app.post decorator.
By defining UserOut without the password field and setting @app.post("/user/", response_model=UserOut), you are instructing FastAPI: "No matter what data the create_user function returns, filter it through the UserOut model before sending it to the client."
This provides a strong guarantee. Even if your function mistakenly returns the full user object with the password, response_model acts as a final safeguard, stripping it out.
The image below shows the resulting API documentation for this endpoint. Notice how the response schema and example value correctly omit the password field.

3. A Practical Walkthrough
Let's see this in action. The following video demonstrates how to define schemas and apply the response_model to GET endpoints, which automatically updates the interactive API documentation.
Python FastAPI Tutorial (Part 4): Pydantic Schemas - Request and Response Validation
This clip from Corey Schafer's tutorial series provides a clear, step-by-step implementation of response models.
Please watch from 03:31 to 12:39, and then from 15:31 to 16:37. Focus on these parts: Schema Definition (03:31 - 10:33): He creates PostBase, PostCreate, and PostResponse. This separation of concerns is exactly the pattern we've discussed. Note his explanation of from_attributes (the modern name for orm_mode), which connects back to our last lesson. Applying response_model (10:33 - 12:39): Watch how he adds response_model=list[PostResponse] and response_model=PostResponse to his GET endpoints. He explains that this not only documents but also validates and filters the output. Viewing the Docs (15:31 - 16:37): See the immediate benefit in the Swagger UI. The response schema is now explicit, showing all the fields, their types, and even validation constraints.
As you can see, the process is straightforward and the benefits are immediate, providing both a safety net for your data and clear documentation for your API consumers.
4. response_model vs. Return Type Annotation
The FastAPI documentation you read earlier also introduced another way to declare a response's shape: using a standard Python return type annotation.
# Option 1: Using the response_model parameter
@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any: # Return type is Any or dict
return item_from_db
# Option 2: Using a return type annotation
@app.post("/items/")
async def create_item(item: Item) -> Item: # Return type is the Pydantic model
return item
So, which should you use?
- Use
response_modelparameter when: The object your function returns is of a different type than your desired response model. A common case is returning a SQLAlchemy ORM object or a plain dictionary, while yourresponse_modelis a PydanticBaseModel. Thefrom_attributes=Trueconfig you learned about is what allows FastAPI to bridge this gap. - Use return type annotation (
-> Item) when: Your function is guaranteed to return an instance of that exact Pydantic model. This provides better support for static analysis tools likemypyand IDEs.
The official documentation offers a best-of-both-worlds approach using class inheritance, which allows you to get both static type checking and FastAPI's data filtering.
Response Model - Return Type - FastAPI
Let's return to the FastAPI documentation to understand this more advanced pattern.
Please read the section 'Return Type and Data Filtering'. This explains how you can create a BaseUser model for the response and a UserIn model that inherits from it for the input, allowing you to use a return type annotation (-> BaseUser) safely.
This inheritance pattern is a clean and robust way to structure your code, and it should feel familiar from your experience with object-oriented programming.
5. Fine-Grained Filtering
While using separate models is the recommended approach for clarity and maintainability, FastAPI provides a few "shortcut" parameters for quick, dynamic filtering on a per-endpoint basis:
response_model_exclude_unset=True: Don't include fields in the response that were not explicitly set (i.e., they are still using their default values). This is great for keeping response payloads lean.response_model_include={"field1", "field2"}: Only include these specific fields in the response.response_model_exclude={"field_to_hide"}: Include all fields except these.
These are analogous to dynamically modifying fields on a DRF serializer instance in a view, but are built directly into the path operation decorator.
A word of caution: While useful, relying heavily on include/exclude can make your API's contract less clear, as the automatically generated OpenAPI documentation will still show the full model. It's often better to define an explicit Pydantic model for the response.
Conclusion
You have now mastered a fundamental aspect of building secure and well-documented FastAPI applications. By controlling the shape of your responses, you dictate the public contract of your API.
Key Takeaways:
- The
response_modelparameter in a path operation decorator is FastAPI's primary tool for filtering, validating, and documenting API responses. - It serves the same purpose as a DRF
Serializerused for output, defining which fields are sent to the client. - Its most critical use case is security: preventing the accidental leakage of sensitive data like passwords or internal IDs by creating separate "output" schemas.
- FastAPI will automatically convert data from other types (like ORM objects or dictionaries) into the
response_modelschema, providedfrom_attributes=Trueis configured. - Using explicit Pydantic models for responses is generally preferred over dynamic
include/excludeparameters for better documentation and clarity.
In our next lesson, we will shift our focus from handling successful responses to handling errors. You will learn how to raise and customize HTTPException for standard error responses, which is FastAPI's equivalent to DRF's APIException classes like NotFound or PermissionDenied.