Secure FastAPI Responses: Separate Input and Output Models with response_model

This article shows how FastAPI's response_model lets you define distinct input and output Pydantic models to hide sensitive fields like passwords, outlines common CRUD scenarios, demonstrates partial updates with PATCH and exclude_unset, and explains why extracting models into a dedicated file improves code organization.

Tech Ocean
Tech Ocean
Tech Ocean
Secure FastAPI Responses: Separate Input and Output Models with response_model

1. Input and Output Models Are Different

When a client registers a user it sends a JSON payload containing email, password, and full_name. The API must not return the password hash. FastAPI handles this by specifying a response_model that defines the shape of the response data.

{
  "email": "[email protected]",
  "password": "secret123",
  "full_name": "Alice"
}

The endpoint returns only the fields defined in the output model:

{
  "email": "[email protected]",
  "full_name": "Alice"
}

Two Pydantic models are defined:

class UserCreate(BaseModel):
    email: EmailStr
    password: str = Field(..., min_length=8)
    full_name: str | None = None

class UserPublic(BaseModel):
    email: EmailStr
    full_name: str | None

The route uses

@app.post("/users", response_model=UserPublic, status_code=201)

. Regardless of what the function returns, FastAPI serializes the result according to UserPublic, so the password field never appears in the response.

2. Model Separation Strategies for Three Common Scenarios

User registration : input model UserCreate, output model UserPublic – password is received but never returned.

Item CRUD : input model ItemCreate (fields name and price), output model ItemResponse (adds id and created_at).

Order creation : input model OrderCreate, output model Order – output includes the automatically calculated total_price.

Example for the item scenario:

class ItemCreate(BaseModel):
    name: str = Field(..., min_length=1)
    price: float = Field(..., gt=0)

class ItemResponse(BaseModel):
    id: int
    name: str
    price: float
    created_at: float

@app.post("/items", response_model=ItemResponse, status_code=201)
def create_item(item: ItemCreate):
    new_item = save_to_db(item)
    return ItemResponse(**new_item)

3. Partial Updates with PATCH and exclude_unset

In REST, PUT replaces the whole resource while PATCH updates only the provided fields. FastAPI can handle partial updates by defining an update model and calling model_dump(exclude_unset=True) to keep only the fields the client sent.

class ItemUpdate(BaseModel):
    name: str | None = None
    price: float | None = None

@app.patch("/items/{item_id}", response_model=ItemResponse)
def update_item(item_id: int, item_update: ItemUpdate):
    for item in ITEMS_DB:
        if item["id"] == item_id:
            item.update(item_update.model_dump(exclude_unset=True))
            return ItemResponse(**item)

The exclude_unset=True flag means only the fields actually supplied by the client are applied, avoiding manual checks such as if "price" in data.

4. Extracting Models to a Separate File

On Day 4 the tutorial moves all Pydantic models into a dedicated models.py file, leaving route logic in main.py:

fastapi-14days/
├── main.py        # routes
├── models.py      # all data models
└── tests/

Benefits of this organization:

Models are immediately visible and not hidden among route code.

The same model can be reused across multiple endpoints.

Tests can import models directly without pulling in routing logic.

5. What Was Done on Day 4

Extracted all Pydantic models into models.py for unified management.

Defined UserCreate, UserPublic, ItemCreate, ItemUpdate, and ItemResponse models.

Used response_model to control the response structure, ensuring passwords never leak.

Implemented a PATCH /items/{id} endpoint for partial updates.

Added seven new test cases, bringing the total to 29 passing tests.

6. Next Steps

Day 5 will cover FastAPI's dependency injection system, showing how to extract reusable logic into independent functions and invoke them from any route.

7. Related Links

Official FastAPI documentation (Chinese):

https://fastapi.tiangolo.com/zh/tutorial/response-model/
https://fastapi.tiangolo.com/zh/tutorial/extra-models/
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythonpatchfastapiresponse_modelpydanticapi-designexclude_unsetmodel-separation
Tech Ocean
Written by

Tech Ocean

Focused on AI programming, sharing ready-to-use development efficiency solutions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.