[{"data":1,"prerenderedAt":99},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Frouting\u002Fresponse-models":3},{"page":4,"siblings":86,"blog":78},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":77,"related":78,"seo":79,"seoDescription":80,"stem":81,"subtopic":6,"topic":82,"topicSlug":83,"updated":84,"__hash__":85},"qa\u002Ffastapi\u002Frouting\u002Fresponse-models.md","Response Models",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,3,"\u002Ffastapi\u002Frouting\u002Fresponse-models",[23,28,32,36,40,44,48,52,56,61,65,69,73],{"id":24,"difficulty":25,"q":26,"a":27},"response-model-basics","easy","What does `response_model` do in a FastAPI route decorator?","`response_model` tells FastAPI which Pydantic model to use for **validating and\nfiltering** the handler's return value before serialising it to JSON.\n\n```python\nclass UserIn(BaseModel):\n    name: str\n    password: str          # sensitive — never leak this\n\nclass UserOut(BaseModel):\n    id: int\n    name: str              # no password field\n\n@app.post(\"\u002Fusers\", response_model=UserOut)\nasync def create_user(user: UserIn) -> UserOut:\n    db_user = await db.create(user)\n    return db_user         # password field is stripped by response_model\n```\n\nFastAPI:\n1. Validates the return value against `UserOut`.\n2. Strips fields not in `UserOut` (e.g., `password`).\n3. Documents `UserOut` as the response schema in OpenAPI.\n\nRule of thumb: always set `response_model` on any endpoint that returns ORM\nobjects or models with sensitive fields.\n",{"id":29,"difficulty":14,"q":30,"a":31},"response-model-vs-return-annotation","What is the difference between `response_model=` and a return type annotation?","Both tell FastAPI the response shape, but `response_model=` **overrides** the\nreturn annotation for serialisation and filtering:\n\n```python\n# return annotation only — used for schema AND serialisation\n@app.get(\"\u002Fusers\u002F{id}\")\nasync def get_user(id: int) -> UserOut:\n    return await db.get(User, id)\n\n# response_model wins — annotation is for type checkers only\n@app.get(\"\u002Fusers\u002F{id}\", response_model=UserOut)\nasync def get_user(id: int) -> User:   # mypy sees User; FastAPI uses UserOut\n    return await db.get(User, id)\n```\n\nUse the return annotation when they're the same type (clean, Pythonic).\nUse `response_model=` when the serialised type differs from what the handler\nactually returns (ORM model → Pydantic output DTO).\n\nRule of thumb: prefer the return annotation; add `response_model=` only when\nyou need to filter or transform the handler's return value.\n",{"id":33,"difficulty":25,"q":34,"a":35},"exclude-none","How do you omit `None` fields from a FastAPI JSON response?","Set `response_model_exclude_none=True` in the route decorator:\n\n```python\nclass Report(BaseModel):\n    total: int\n    average: float | None = None\n    notes: str | None = None\n\n@app.get(\"\u002Freport\", response_model=Report, response_model_exclude_none=True)\nasync def get_report():\n    return {\"total\": 100}\n    # average and notes are None → omitted: {\"total\": 100}\n```\n\nRule of thumb: use `exclude_none=True` for sparse responses where `None`\nmeans \"not applicable\" — keeps the JSON payload small and clean.\n",{"id":37,"difficulty":14,"q":38,"a":39},"exclude-unset","What is `response_model_exclude_unset` and when is it useful?","`response_model_exclude_unset=True` strips fields that were **not explicitly\nset** in the return value — it distinguishes between \"not set\" and \"set to None\".\n\n```python\nclass Item(BaseModel):\n    name: str\n    description: str | None = None\n    price: float | None = None\n\n@app.patch(\"\u002Fitems\u002F{id}\", response_model=Item, response_model_exclude_unset=True)\nasync def patch_item(id: int, patch: Item):\n    existing = await db.get(id)\n    updated_data = patch.model_dump(exclude_unset=True)\n    existing.update(updated_data)\n    return existing\n```\n\nThis is critical for PATCH semantics: you only return (and store) fields the\nclient actually sent, not defaults.\n\nRule of thumb: use `exclude_unset=True` on PATCH endpoints — it lets clients\nsend partial updates without overwriting fields they didn't mention.\n",{"id":41,"difficulty":14,"q":42,"a":43},"response-model-exclude","How do you always exclude specific fields from the response without a separate output model?","Use `response_model_exclude={\"field1\", \"field2\"}` in the route decorator:\n\n```python\nclass User(BaseModel):\n    id: int\n    name: str\n    password_hash: str\n    internal_notes: str | None = None\n\n@app.get(\n    \"\u002Fusers\u002F{id}\",\n    response_model=User,\n    response_model_exclude={\"password_hash\", \"internal_notes\"},\n)\nasync def get_user(id: int):\n    return await db.get(id)\n```\n\nThis avoids creating a separate `UserOut` model for simple exclusion cases.\nFor complex filtering (renames, computed fields), a dedicated output model is cleaner.\n\nRule of thumb: use `response_model_exclude` for 1-2 fields; create a dedicated\noutput model when you exclude three or more fields or need renames\u002Faliases.\n",{"id":45,"difficulty":14,"q":46,"a":47},"json-response","When should you return `JSONResponse` directly instead of a dict or Pydantic model?","Return `JSONResponse` when you need to:\n- Set a **non-default status code** dynamically.\n- Set **custom response headers**.\n- Bypass `response_model` filtering (e.g., a pre-serialised payload).\n\n```python\nfrom fastapi.responses import JSONResponse\n\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int):\n    item = await db.get(id)\n    if not item:\n        return JSONResponse(\n            status_code=404,\n            content={\"detail\": \"Not found\"},\n            headers={\"X-Request-ID\": \"abc123\"},\n        )\n    return item   # normal path uses response_model\n```\n\n`JSONResponse` bypasses Pydantic serialisation — pass JSON-safe Python primitives\nor use `jsonable_encoder()` first.\n\nRule of thumb: return dicts\u002Fmodels 95% of the time; reach for `JSONResponse`\nonly when you need direct control over status code or headers.\n",{"id":49,"difficulty":14,"q":50,"a":51},"response-classes","What response classes does FastAPI\u002FStarlette provide and when do you use each?","| Class | Use case |\n|-------|----------|\n| `JSONResponse` (default) | JSON API responses |\n| `HTMLResponse` | Rendered HTML pages |\n| `PlainTextResponse` | Plain text, logs |\n| `RedirectResponse` | 301\u002F302\u002F307 redirects |\n| `FileResponse` | Send a file with proper headers |\n| `StreamingResponse` | Large responses, real-time data |\n| `ORJSONResponse` | Faster JSON via `orjson` |\n| `UJSONResponse` | Faster JSON via `ujson` |\n\n```python\nfrom fastapi.responses import FileResponse, StreamingResponse\n\n@app.get(\"\u002Fdownload\")\nasync def download():\n    return FileResponse(\"report.pdf\", filename=\"report.pdf\")\n\n@app.get(\"\u002Fstream\")\nasync def stream():\n    async def generate():\n        for chunk in large_data():\n            yield chunk\n    return StreamingResponse(generate(), media_type=\"text\u002Fplain\")\n```\n\nRule of thumb: set `default_response_class=ORJSONResponse` on the `FastAPI()`\ninstance to get faster serialisation across all endpoints with no other changes.\n",{"id":53,"difficulty":14,"q":54,"a":55},"orjson-response","How do you use `ORJSONResponse` for faster JSON serialisation in FastAPI?","`orjson` is a Rust-backed JSON library that is 5-10× faster than the stdlib\n`json` module. Install it with `pip install orjson`, then:\n\n```python\nfrom fastapi import FastAPI\nfrom fastapi.responses import ORJSONResponse\n\n# Make it the default for all routes\napp = FastAPI(default_response_class=ORJSONResponse)\n\n@app.get(\"\u002Fitems\")\nasync def list_items():\n    return [{\"id\": 1}, {\"id\": 2}]\n```\n\nOr per-route:\n```python\n@app.get(\"\u002Fitems\", response_class=ORJSONResponse)\nasync def list_items():\n    ...\n```\n\n`orjson` natively handles `datetime`, `UUID`, `bytes` and NumPy arrays without\n`jsonable_encoder`.\n\nRule of thumb: switch to `ORJSONResponse` globally for any throughput-sensitive\nAPI — it's a one-line change with measurable latency improvement.\n",{"id":57,"difficulty":58,"q":59,"a":60},"streaming-response","hard","How do you stream a large response in FastAPI without loading it all into memory?","Return a `StreamingResponse` with an async (or sync) generator:\n\n```python\nfrom fastapi.responses import StreamingResponse\nimport asyncio\n\nasync def generate_csv():\n    yield \"id,name\\n\"\n    async for row in db.stream_all_users():\n        yield f\"{row.id},{row.name}\\n\"\n\n@app.get(\"\u002Fexport\u002Fusers.csv\")\nasync def export_users():\n    return StreamingResponse(\n        generate_csv(),\n        media_type=\"text\u002Fcsv\",\n        headers={\"Content-Disposition\": \"attachment; filename=users.csv\"},\n    )\n```\n\nThe client receives bytes as they arrive; the server never holds the full dataset\nin memory.\n\nRule of thumb: use `StreamingResponse` for exports, large file downloads, and\nserver-sent events — never load a 1M-row dataset into a list before sending.\n",{"id":62,"difficulty":25,"q":63,"a":64},"redirect-response","How do you issue a redirect from a FastAPI route?","Return a `RedirectResponse`:\n\n```python\nfrom fastapi.responses import RedirectResponse\nfrom fastapi import status\n\n@app.get(\"\u002Fold-path\")\nasync def old_path():\n    return RedirectResponse(\n        url=\"\u002Fnew-path\",\n        status_code=status.HTTP_301_MOVED_PERMANENTLY,\n    )\n\n# Temporary redirect (default 307)\n@app.get(\"\u002Flogin\")\nasync def login_redirect():\n    return RedirectResponse(url=\"\u002Fauth\u002Flogin\")\n```\n\nDefault status code for `RedirectResponse` is **307 Temporary Redirect**, which\npreserves the HTTP method. Use 301 for permanent SEO-friendly redirects, 302 for\ntemporary, 303 to force GET on redirect.\n\nRule of thumb: use 307 to preserve POST method on redirect; use 303 to convert\na POST response to a GET (Post\u002FRedirect\u002FGet pattern).\n",{"id":66,"difficulty":14,"q":67,"a":68},"response-model-include","How do you include only specific fields in the response (whitelist instead of blacklist)?","Use `response_model_include={\"field1\", \"field2\"}`:\n\n```python\nclass User(BaseModel):\n    id: int\n    name: str\n    email: str\n    phone: str | None = None\n    internal_id: str\n\n@app.get(\n    \"\u002Fusers\u002F{id}\u002Fpublic\",\n    response_model=User,\n    response_model_include={\"id\", \"name\"},   # only these two fields\n)\nasync def get_user_public(id: int):\n    return await db.get(id)\n# response: {\"id\": 42, \"name\": \"Alice\"}\n```\n\nRule of thumb: prefer `response_model_exclude` for \"strip secrets\" use cases and\na dedicated output model for complex projections — `response_model_include` with\nmany field names becomes hard to maintain.\n",{"id":70,"difficulty":14,"q":71,"a":72},"custom-response-headers","How do you add custom headers to a FastAPI response?","Inject the `Response` object into the handler and set headers on it:\n\n```python\nfrom fastapi import Response\n\n@app.get(\"\u002Fitems\")\nasync def list_items(response: Response):\n    items = await db.all()\n    response.headers[\"X-Total-Count\"] = str(len(items))\n    response.headers[\"Cache-Control\"] = \"max-age=60\"\n    return items   # normal JSON response with extra headers\n```\n\nYou can also set headers via `JSONResponse(headers={...})` or middleware.\nInjecting `Response` is the cleanest option when the route already returns a\nplain dict\u002Fmodel.\n\nRule of thumb: inject `Response` for per-request headers (pagination counts,\nrate-limit info); use middleware for headers that apply to all responses (CORS, CSP).\n",{"id":74,"difficulty":14,"q":75,"a":76},"background-tasks-response","How does a route return a response immediately while also scheduling a background task?","Inject `BackgroundTasks` and add tasks before returning:\n\n```python\nfrom fastapi import BackgroundTasks\n\ndef notify_admin(item_id: int):\n    send_email(\"admin@example.com\", f\"New item {item_id} created\")\n\n@app.post(\"\u002Fitems\", status_code=201)\nasync def create_item(item: Item, background_tasks: BackgroundTasks):\n    new_item = await db.create(item)\n    background_tasks.add_task(notify_admin, new_item.id)\n    return new_item   # response is sent immediately; email sent after\n```\n\nFastAPI sends the HTTP response, then runs the background task(s) sequentially.\nThe client gets its 201 Created without waiting for the email.\n\nRule of thumb: use `BackgroundTasks` for fast, non-retryable work; use a task\nqueue (Celery, ARQ) for anything that must succeed or be retried.\n",13,null,{"description":11},"FastAPI response model interview questions — response_model, filtering fields, exclude_none, exclude_unset, JSONResponse, custom response classes and status codes.","fastapi\u002Frouting\u002Fresponse-models","Routing & Parameters","routing","2026-06-20","czdTRQmnhNWC4pVOTDpgTUqZ_vSQlGS0ToZb22Gtox4",[87,91,94,95],{"subtopic":88,"path":89,"order":90},"Path & Query Parameters","\u002Ffastapi\u002Frouting\u002Fpath-query-params",1,{"subtopic":92,"path":93,"order":12},"Request Body","\u002Ffastapi\u002Frouting\u002Frequest-body",{"subtopic":6,"path":21,"order":20},{"subtopic":96,"path":97,"order":98},"Routers & Structure","\u002Ffastapi\u002Frouting\u002Frouters",4,1782244112734]