File size: 4,524 Bytes
a152b95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# 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.