FastAPI Response Model Fix
In this tutorial, you'll learn about FastAPI Response Model Fix. We cover key concepts, practical examples, and best practices.
The Problem
FastAPI endpoints return Pydantic models by default, but they may expose internal fields (password hashes, secret IDs) or include fields you don't want the client to see.
Quick Fix
Wrong — returning the full model
from pydantic import BaseModel
class User(BaseModel):
id: int
username: str
email: str
password_hash: str # Exposed to client!
@app.get("/users/{user_id}")
async def get_user(user_id: int):
user = await fetch_user(user_id)
return user # Returns password_hash to client
Output: API response includes password_hash. A serious security vulnerability.
Correct — response_model filtering
from pydantic import BaseModel
class UserInDB(BaseModel):
id: int
username: str
email: str
password_hash: str
class UserOut(BaseModel):
id: int
username: str
email: str
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
user = await fetch_user(user_id) # Returns UserInDB
return user # FastAPI filters to UserOut fields
Output: Response only includes id, username, email. password_hash is excluded.
response_model with list
@app.get("/users", response_model=List[UserOut])
async def list_users():
users = await fetch_all_users()
return users
response_model_exclude and include
@app.get(
"/users/{user_id}",
response_model=UserOut,
response_model_exclude={"email"}, # Exclude email from this response
)
async def get_user_public(user_id: int):
user = await fetch_user(user_id)
return user
Different responses for different status codes
from fastapi.responses import JSONResponse
class ErrorResponse(BaseModel):
error: str
code: int
@app.get(
"/users/{user_id}",
response_model=UserOut,
responses={
404: {"model": ErrorResponse},
},
)
async def get_user(user_id: int):
user = await fetch_user(user_id)
if not user:
return JSONResponse(status_code=404, content={"error": "Not found", "code": 404})
return user
Response model with ORM mode
class UserOut(BaseModel):
id: int
username: str
email: str
class Config:
from_attributes = True # Enables ORM mode
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
user = await db.query(UserORM).filter(UserORM.id == user_id).first()
return user # ORM object converted to Pydantic automatically
Prevention
- Always define a separate response model that excludes sensitive fields.
- Use
response_modelon every endpoint that returns data. - Use
from_attributes = Truein response models for ORM integration.
Common Mistakes with response model
- Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
These mistakes appear frequently in real-world FASTAPI code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro