# Response Model in FastAPI The `response_model` parameter on route decorators lets you declare the shape of the data your endpoint returns. FastAPI uses it to validate, serialize, and document the response -- filtering out any fields not defined in the model and generating accurate OpenAPI schemas. ## Basic Response Model ```python from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class UserIn(BaseModel): username: str email: str password: str class UserOut(BaseModel): username: str email: str @app.post("/users/", response_model=UserOut, status_code=201) async def create_user(user: UserIn): # In a real app, hash the password and save to DB return user # password is automatically filtered out ``` Even though the handler returns the full `UserIn` object (which includes `password`), the `response_model=UserOut` declaration ensures that only `username` and `email` appear in the response. This is a critical security pattern -- it prevents accidental leakage of sensitive fields like passwords, tokens, or internal IDs. ## Status Codes FastAPI provides the `status_code` parameter to set the HTTP response status code. Common codes include: | Code | Constant | Usage | |------|------------------------------------|----------------------| | 200 | `status.HTTP_200_OK` | Successful GET | | 201 | `status.HTTP_201_CREATED` | Successful creation | | 204 | `status.HTTP_204_NO_CONTENT` | Successful deletion | | 400 | `status.HTTP_400_BAD_REQUEST` | Client error | | 404 | `status.HTTP_404_NOT_FOUND` | Resource not found | | 422 | `status.HTTP_422_UNPROCESSABLE_ENTITY` | Validation error | ```python from fastapi import status @app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_item(item_id: int): # delete logic return None ``` The default `status_code` for all route decorators is `200`. ## Filtering Fields with response_model_include and response_model_exclude You can dynamically control which fields appear in the response without creating a separate model: ```python class Item(BaseModel): name: str description: str | None = None price: float tax: float = 0.0 internal_code: str = "N/A" @app.get( "/items/{item_id}", response_model=Item, response_model_exclude={"internal_code"}, ) async def read_item(item_id: int): return { "name": "Widget", "description": "A useful widget", "price": 35.99, "tax": 3.60, "internal_code": "WDG-001", } ``` The `response_model_exclude` parameter accepts a `set` of field names to strip from the output. Similarly, `response_model_include` accepts a `set` of field names to keep -- all others are excluded. If both are provided, `response_model_include` is applied first, then `response_model_exclude` removes fields from that subset. ## Excluding Unset and Default Values Two additional parameters control whether default or unset values appear in the response: ```python @app.get( "/items/{item_id}", response_model=Item, response_model_exclude_unset=True, ) async def read_item(item_id: int): return Item(name="Widget", price=35.99) # Response: {"name": "Widget", "price": 35.99} # Fields with defaults (description, tax) are omitted ``` - `response_model_exclude_unset=True` -- omits fields the user did not explicitly set (default: `False`) - `response_model_exclude_defaults=True` -- omits fields whose value matches the default (default: `False`) - `response_model_exclude_none=True` -- omits fields with `None` values (default: `False`) ## Multiple Response Models Use `Union` types or the `responses` parameter to document endpoints that may return different shapes: ```python from typing import Union class ItemPublic(BaseModel): name: str price: float class ItemAdmin(BaseModel): name: str price: float internal_code: str profit_margin: float @app.get("/items/{item_id}", response_model=Union[ItemAdmin, ItemPublic]) async def read_item(item_id: int, is_admin: bool = False): item_data = get_item(item_id) if is_admin: return ItemAdmin(**item_data) return ItemPublic(**item_data) ``` When using `Union`, Pydantic validates the response against each model in order and uses the first match. Place the more specific model first (the one with more fields) to avoid premature matching.