Response Models & Status Codes — Shaping What Goes Out
Intermediateresponse_model filters and documents what an endpoint returns — the classic win is UserOut without the password hash. status_code sets the right code per operation; HTTPException handles the sad paths.
Overview
Input validation is half the story; the response deserves the same rigour. response_model declares the output schema: FastAPI serializes through it, silently dropping any field not in the model — which is how you guarantee a password hash or internal flag never leaks, even if your function returns the full database object. Separate In/Out models (UserIn with password, UserOut without) are the standard pattern, with a shared base class to avoid repetition. status_code fixes the success code (201 for creation, 204 for deletion), and raising HTTPException produces clean error responses from anywhere in the call stack. Together these make the OpenAPI docs a true contract: exact shapes in, exact shapes out, per status code.
response_model — the Output Filter
Return anything; FastAPI reshapes it through response_model. Fields not in the model are removed from the response — leaking is structurally impossible, and the docs show the real output schema.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
name: str
email: EmailStr
class UserIn(UserBase): # what the client SENDS
password: str
class UserOut(UserBase): # what the client GETS — no password
id: int
fake_db = {}
@app.post("/users", response_model=UserOut, status_code=201)
def register(user: UserIn):
hashed = "bcrypt$" + user.password[::-1] # (real hashing → Auth chapter)
record = {"id": len(fake_db) + 1, **user.model_dump(), "password": hashed}
fake_db[record["id"]] = record
return record # ← contains password! response_model strips it.
# POST /users {"name":"Asha","email":"asha@iitb.ac.in","password":"s3cret"}
# → 201 {"id": 1, "name": "Asha", "email": "asha@iitb.ac.in"}
# password never leaves the server — enforced by the framework, not discipline
# Lists work too:
@app.get("/users", response_model=list[UserOut])
def all_users():
return list(fake_db.values())
# Skip nulls in output: response_model_exclude_none=TrueStatus Codes and HTTPException
Success codes are part of the contract: 201 Created, 204 No Content, 200 everything else. For failures, raise HTTPException — it short-circuits from any depth and renders {"detail": ...} with your code.
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
orders = {1: {"item": "Kota doria saree", "amount": 1499}}
@app.get("/orders/{order_id}")
def get_order(order_id: int):
order = orders.get(order_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, # named constants read better
detail=f"Order {order_id} not found",
)
return order
# GET /orders/99 → 404 {"detail": "Order 99 not found"}
@app.post("/orders", status_code=status.HTTP_201_CREATED)
def create_order(payload: dict):
return payload # → 201, not default 200
@app.delete("/orders/{order_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_order(order_id: int):
orders.pop(order_id, None)
return None # 204 = empty body
# The cheat-sheet that interviewers expect:
# 200 OK read/update success 400 bad request (semantic)
# 201 Created POST success 401 not logged in
# 204 No Content DELETE success 403 logged in, not allowed
# 422 validation (FastAPI automatic) 404 not found · 409 conflict
# 500 bug — never raise on purposeKey Points to Remember
- 1response_model filters output — extra fields (password!) are dropped, guaranteed
- 2Separate In/Out models sharing a base class is the standard pattern
- 3Set status_code per operation: 201 create, 204 delete, 200 default
- 4raise HTTPException(status_code, detail) from any depth for error responses
Interview Questions
Sign in to ask AriaHow do you guarantee a password hash can never appear in an API response?
401 vs 403 vs 404 — give a concrete scenario for each in an orders API.
Why are separate UserIn and UserOut models better than one User model with optional fields?
Ask Aria about Response Models & Status Codes — Shaping What Goes Out
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.