Complete worked example
python
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
from pydantic import BaseModel
class PublicRole(BaseModel):
id: int
title: str
def inspect_example(role_id):
records = {1: {"id": 1, "title": "QA", "country": "IL", "private_note": "demo-only"},
2: {"id": 2, "title": "API", "country": "UA", "private_note": "hidden"}}
app = FastAPI()
@app.get("/roles/{role_id}", response_model=PublicRole)
def read(role_id: int):
if role_id not in records:
raise HTTPException(status_code=404, detail="Role not found")
record = records[role_id]
return record
with TestClient(app) as client:
response = client.get(f"/roles/{role_id}")
return {"status": response.status_code, "body": response.json()}- Reasoning
- The route returns an ordinary mapping and FastAPI applies PublicRole. A missing record raises HTTPException rather than returning an invented role. The helper reports the exact response so a leaked field is observable, not filtered away again by test code. Returning a Response or JSONResponse directly takes a different path: the framework treats it as an already prepared response. Do not assume the normal model projection will sanitize such a response. The counterexample deliberately returns JSONResponse(record), which exposes private_note and fails the expected public contract.