Lesson 4 / 26
Request and Response Models With Pydantic
Validate in, filter out.
BaseModel, Field, response_model
Request bodies are Pydantic models: fields with types and constraints (Field(min_length=2), gt=0) are validated before your code runs, and invalid input returns 422 with every problem listed. A response_model (or return type annotation) filters and serialises the output, so internal fields such as costs or password hashes never leak. Set status_code=201 for creation endpoints. Keep separate models for input and output.
Models, dependencies, async, docs
Pydantic models validate data, dependencies share logic, async handles I/O concurrency, and OpenAPI documents it all.
Validation and output filtering, run
I ran this with Python 3.12, FastAPI 0.142.2, Pydantic 2.13 and Starlette 1.7, calling the app through FastAPI's TestClient (no server needed). A valid product returns 201 and the internal_cost field is stripped by the response model. An invalid body returns 422 listing both the too-short name and the non-positive price.
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
class ProductIn(BaseModel):
name: str = Field(min_length=2, max_length=50)
price: float = Field(gt=0)
tags: list[str] = []
class ProductOut(BaseModel):
id: int
name: str
price: float
app = FastAPI()
DB: dict[int, dict] = {}
@app.post("/products", response_model=ProductOut, status_code=201)
def create(p: ProductIn):
pid = len(DB) + 1
DB[pid] = {"id": pid, **p.model_dump(), "internal_cost": 42} # extra field
return DB[pid] # response_model filters the output
client = TestClient(app)
r = client.post("/products", json={"name": "Pen", "price": 899, "tags": ["office"]})
print(r.status_code, r.json())
r = client.post("/products", json={"name": "X", "price": -5})
print(r.status_code, [(e["loc"][-1], e["msg"]) for e in r.json()["detail"]])
Output:
201 {'id': 1, 'name': 'Pen', 'price': 899.0}
422 [('name', 'String should have at least 2 characters'), ('price', 'Input should be greater than 0')]Separate input and output models
ProductIn, ProductOut and ProductDB models make it obvious what clients can send and what they receive.
Quick check: What does response_model do with fields not in the model?
- Adds them as strings
- Removes them from the response
- Raises an error
- Encrypts them
Answer
Removes them from the response — Output filtering prevents leaks.