[{"data":1,"prerenderedAt":396},["ShallowReactive",2],{"topic-fastapi-fundamentals":3},{"framework":4,"topic":15,"subtopics":23},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Ffastapi.yml","FastAPI interview questions on async routing, Pydantic validation, dependency injection, OAuth2 security, database integration and deployment — the go-to Python framework for production APIs.","yml","fastapi",{},"FastAPI",6,"frameworks\u002Ffastapi",1,"lgr_X74wBdBYovbrlazGWPWqqi-YwNEUq44l1BmtgyE",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":13,"slug":20,"stem":21,"__hash__":22},"topics\u002Ftopics\u002Ffastapi-fundamentals.yml","Async\u002Fawait, ASGI, the request lifecycle and OpenAPI — the FastAPI foundation every interviewer expects you to understand cold.",{},"Fundamentals","fundamentals","topics\u002Ffastapi-fundamentals","L8aL7gSWL5pRFH-IJgI0dL4rZBXrNTnrnAiFK5-7hd8",[24,109,180,255,327],{"id":25,"title":26,"body":27,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":37,"order":13,"path":38,"questions":39,"questionsCount":102,"related":103,"seo":104,"seoDescription":105,"stem":106,"subtopic":26,"topic":19,"topicSlug":20,"updated":107,"__hash__":108},"qa\u002Ffastapi\u002Ffundamentals\u002Fasync-basics.md","Async Basics",{"type":28,"value":29,"toc":30},"minimark",[],{"title":31,"searchDepth":32,"depth":32,"links":33},"",2,[],"medium","md",{},true,"\u002Ffastapi\u002Ffundamentals\u002Fasync-basics",[40,45,49,53,57,61,66,70,74,78,82,86,90,94,98],{"id":41,"difficulty":42,"q":43,"a":44},"what-is-async-await","easy","What is async\u002Fawait in Python and why does FastAPI use it?","`async`\u002F`await` is Python's syntax for **cooperative concurrency**. An `async def`\nfunction returns a **coroutine** object; `await` suspends that coroutine until the\nawaited operation completes, letting the event loop run other coroutines in the\nmeantime. No thread switch happens — the same OS thread handles many requests.\n\n```python\nimport asyncio\n\nasync def fetch_user(user_id: int) -> dict:\n    await asyncio.sleep(0.1)   # yield control while \"waiting\"\n    return {\"id\": user_id}\n```\n\nFastAPI is built on top of **Starlette** and **asyncio**. When you declare a route\nhandler as `async def`, FastAPI runs it directly on the event loop, giving you\nhigh concurrency without spawning new threads for each request.\n\nRule of thumb: use `async def` handlers whenever you `await` something (DB call,\nHTTP request, cache); use `def` for pure CPU work that has no async alternative.\n",{"id":46,"difficulty":34,"q":47,"a":48},"asgi-vs-wsgi","What is the difference between ASGI and WSGI?","**WSGI** (Web Server Gateway Interface, PEP 3333) is the classic Python web\nserver standard. It assumes a **synchronous**, request-per-thread model —\na server calls `app(environ, start_response)` and blocks until the response is\nreturned. Frameworks like Flask and Django (pre-4.x) use WSGI.\n\n**ASGI** (Asynchronous Server Gateway Interface) extends WSGI to support\n**async handlers, WebSockets and long-polling** in the same interface. A server\ncalls `await app(scope, receive, send)` and the app may `await` I\u002FO freely.\n\n```python\n# Minimal ASGI app (what Starlette\u002FFastAPI is under the hood)\nasync def app(scope, receive, send):\n    if scope[\"type\"] == \"http\":\n        await send({\"type\": \"http.response.start\", \"status\": 200, \"headers\": []})\n        await send({\"type\": \"http.response.body\", \"body\": b\"Hello\"})\n```\n\nFastAPI is an ASGI framework served by **Uvicorn** (or Hypercorn). ASGI lets a\nsingle process handle thousands of simultaneous connections without one thread\nper connection.\n\nRule of thumb: WSGI = sync\u002Fthreads; ASGI = async\u002Fevent-loop; FastAPI requires ASGI.\n",{"id":50,"difficulty":34,"q":51,"a":52},"event-loop","What is the Python event loop and how does FastAPI interact with it?","The **event loop** is a scheduler that runs coroutines, handles I\u002FO callbacks\nand timers — all in a single thread. When a coroutine `await`s an I\u002FO operation,\nthe loop suspends it and runs the next ready coroutine.\n\n```python\nimport asyncio\n\nasync def task_a():\n    print(\"A start\")\n    await asyncio.sleep(1)   # loop runs task_b here\n    print(\"A done\")\n\nasync def task_b():\n    print(\"B start\")\n    await asyncio.sleep(0.5)\n    print(\"B done\")\n\nasyncio.run(asyncio.gather(task_a(), task_b()))\n# prints: A start, B start, B done, A done\n```\n\nUvicorn creates one event loop per worker process and passes every incoming HTTP\nrequest into it. FastAPI schedules your `async def` handler on that loop. If you\nblock the loop (e.g., call `time.sleep()` or a synchronous DB driver), *all*\nrequests stall until the block clears.\n\nRule of thumb: never call blocking code directly from an `async def` handler —\nuse `await asyncio.to_thread()` or a thread pool executor instead.\n",{"id":54,"difficulty":42,"q":55,"a":56},"coroutine-vs-function","What is a coroutine and how is it different from a regular function?","A **coroutine** is created by an `async def` function. Calling it does **not**\nexecute the body — it returns a coroutine object. The body only runs when you\n`await` it (or pass it to `asyncio.run()`).\n\n```python\ndef regular():\n    return 42          # runs immediately on call\n\nasync def coro():\n    return 42          # returns \u003Ccoroutine object> on call\n\nresult = regular()     # 42\nobj    = coro()        # \u003Ccoroutine object coro at 0x...> — nothing ran yet\nresult = await coro()  # 42 — body now runs\n```\n\nFastAPI recognises `async def` route handlers and schedules them with `await`\ninternally. If you declare a route as `def`, FastAPI runs it in a thread pool\nto avoid blocking the event loop.\n\nRule of thumb: a coroutine is a pauseable function; without `await` (or\n`asyncio.run`), its body never executes.\n",{"id":58,"difficulty":34,"q":59,"a":60},"sync-vs-async-handler","When should you use `def` vs `async def` for a FastAPI route handler?","| Handler | When to use | FastAPI runs it in |\n|---------|-------------|--------------------|\n| `async def` | calls `await`-able I\u002FO (DB, HTTP, cache) | event loop directly |\n| `def` | CPU work or sync libraries (Pandas, Pillow) | thread-pool executor |\n\n```python\n# async — awaits the DB; loop stays free\n@app.get(\"\u002Fusers\u002F{id}\")\nasync def get_user(id: int, db: AsyncSession = Depends(get_db)):\n    return await db.get(User, id)\n\n# def — pandas is sync-only; FastAPI offloads to a thread\n@app.post(\"\u002Freport\")\ndef generate_report(payload: ReportRequest):\n    df = pd.read_csv(payload.path)   # blocking I\u002FO, but in a thread\n    return df.describe().to_dict()\n```\n\nThe wrong choice either wastes threads (`async def` + sync library that blocks)\nor adds unnecessary thread overhead (`def` + code that could have been `await`ed).\n\nRule of thumb: if you `await` anything inside the handler, declare it `async def`;\nif the handler is pure CPU\u002Fsync, use `def`.\n",{"id":62,"difficulty":63,"q":64,"a":65},"blocking-event-loop","hard","What happens if you block the event loop in FastAPI, and how do you fix it?","Blocking the event loop means calling any operation that occupies the thread\nwithout yielding: `time.sleep()`, synchronous file reads, `requests.get()`, heavy\ncomputation. While blocked, **no other coroutine runs** — all concurrent requests\nstall until the block clears.\n\n```python\n# BAD — blocks the event loop for every request\n@app.get(\"\u002Fslow\")\nasync def slow():\n    time.sleep(2)          # freezes ALL requests for 2 s\n    return {\"ok\": True}\n\n# GOOD — moves the blocking call to a thread\n@app.get(\"\u002Fslow\")\nasync def slow():\n    await asyncio.to_thread(time.sleep, 2)   # only this coroutine pauses\n    return {\"ok\": True}\n```\n\nOther fixes:\n- Switch to an async library (`httpx` instead of `requests`, `asyncpg` instead of `psycopg2`).\n- Use `loop.run_in_executor(None, blocking_fn)` (older equivalent of `asyncio.to_thread`).\n- Declare the handler as plain `def` — FastAPI automatically runs it in a thread pool.\n\nRule of thumb: if you can't avoid a blocking call inside `async def`, use\n`asyncio.to_thread()` to push it off the event loop.\n",{"id":67,"difficulty":34,"q":68,"a":69},"asyncio-gather","What does `asyncio.gather()` do and when would you use it in FastAPI?","`asyncio.gather(*coros)` runs multiple coroutines **concurrently on the same\nevent loop** and returns their results as a list in the same order as the inputs.\nIt's the idiomatic way to fan-out I\u002FO work within a single handler.\n\n```python\n@app.get(\"\u002Fdashboard\u002F{user_id}\")\nasync def dashboard(user_id: int):\n    # fire both DB queries at the same time instead of sequentially\n    user, orders = await asyncio.gather(\n        fetch_user(user_id),\n        fetch_orders(user_id),\n    )\n    return {\"user\": user, \"orders\": orders}\n```\n\nSequential awaits would take `t_user + t_orders`; `gather` takes `max(t_user, t_orders)`.\n\nRule of thumb: use `asyncio.gather()` whenever two or more I\u002FO calls are\nindependent — it cuts latency to the slowest one instead of the sum.\n",{"id":71,"difficulty":63,"q":72,"a":73},"anyio-trio","What is anyio and how does it relate to FastAPI?","**anyio** is a compatibility shim that lets library authors write async code once\nand run it on **asyncio** *or* **Trio** without changes. Starlette (FastAPI's\nfoundation) adopted anyio as its async backend in v0.20+.\n\n```python\nimport anyio\n\nasync def run_parallel():\n    async with anyio.create_task_group() as tg:\n        tg.start_soon(task_a)\n        tg.start_soon(task_b)\n```\n\nIn practice for FastAPI users:\n- You still write plain `asyncio` code — anyio is transparent.\n- `pytest-anyio` \u002F `anyio.from_thread.run_sync` become relevant when testing.\n- Starlette's `BackgroundTask` and `lifespan` use anyio task groups internally.\n\nRule of thumb: you rarely call anyio directly in application code; knowing it\nexists explains why `pytest-anyio` is the recommended test runner for Starlette apps.\n",{"id":75,"difficulty":34,"q":76,"a":77},"run-sync-in-async","How do you call a synchronous function from an async context without blocking?","Use `asyncio.to_thread()` (Python 3.9+) to run a sync function in the default\nthread-pool executor while keeping the event loop free.\n\n```python\nimport asyncio\n\ndef sync_heavy(n: int) -> int:\n    # CPU or blocking I\u002FO, e.g. reading a large file\n    return sum(range(n))\n\n@app.get(\"\u002Fcompute\")\nasync def compute(n: int):\n    result = await asyncio.to_thread(sync_heavy, n)\n    return {\"result\": result}\n```\n\nFor older Python (3.7–3.8) use `loop.run_in_executor(None, fn, *args)`.\nFor FastAPI specifically, declaring the route as plain `def` achieves the same\nthing automatically — FastAPI calls `run_in_executor` for you.\n\nRule of thumb: prefer `def` route handlers for wholly-sync work; use\n`asyncio.to_thread()` only when you're already in an `async def` and need\none sync call inside otherwise-async logic.\n",{"id":79,"difficulty":34,"q":80,"a":81},"httpx-vs-requests","Why use httpx instead of requests in a FastAPI application?","`requests` is **synchronous** — calling `requests.get()` blocks the OS thread\nuntil the response arrives. In an `async def` handler that means the event loop\nis frozen for the duration of every outbound HTTP call.\n\n`httpx` provides an identical API *plus* an `AsyncClient` that integrates with\nasyncio:\n\n```python\nimport httpx\n\n@app.get(\"\u002Fproxy\")\nasync def proxy(url: str):\n    async with httpx.AsyncClient() as client:\n        resp = await client.get(url)   # loop stays free\n    return resp.json()\n```\n\n`httpx` also supports HTTP\u002F2, connection pooling via a shared client, and\n`httpx.AsyncClient` as a test transport for FastAPI's `TestClient`.\n\nRule of thumb: always use `httpx.AsyncClient` for outbound HTTP inside FastAPI\nasync handlers; use `requests` only in plain `def` routes or CLI scripts.\n",{"id":83,"difficulty":42,"q":84,"a":85},"what-is-starlette","What is Starlette and what does FastAPI add on top of it?","**Starlette** is a lightweight ASGI framework\u002Ftoolkit that provides routing,\nmiddleware, WebSockets, background tasks and static files. FastAPI is built\n**directly on top of Starlette** — every `FastAPI()` instance is a Starlette app.\n\nFastAPI's additions:\n- **Type-hint-driven parameter parsing** — path, query, body extracted via annotations.\n- **Pydantic v2 validation** — automatic request validation and serialization.\n- **Dependency injection** via `Depends()`.\n- **Automatic OpenAPI \u002F JSON Schema** generation (Swagger UI, ReDoc).\n- `HTTPException` helpers and response model enforcement.\n\n```python\nfrom fastapi import FastAPI\nfrom starlette.applications import Starlette   # same base class\n```\n\nRule of thumb: think of FastAPI as Starlette + Pydantic + OpenAPI; you can use\nall Starlette primitives (middleware, WebSockets, mounts) in a FastAPI app.\n",{"id":87,"difficulty":63,"q":88,"a":89},"concurrency-model","Describe FastAPI's concurrency model end-to-end.","1. **Uvicorn** (ASGI server) runs one event loop per worker process.\n2. A new HTTP connection arrives; Uvicorn's event loop accepts it and parses headers.\n3. FastAPI's router matches the URL and calls the handler.\n4. If the handler is `async def` → FastAPI `await`s it on the event loop.\n   If the handler is `def` → FastAPI submits it to `anyio`'s thread pool and\n   `await`s the future, keeping the event loop free.\n5. The handler performs I\u002FO (e.g., `await db.execute()`). The coroutine suspends\n   and the loop picks up the next ready coroutine.\n6. When I\u002FO completes, the handler resumes and returns a response dict.\n7. FastAPI serializes it (Pydantic `model_dump` → JSON) and sends bytes back\n   through Uvicorn.\n\n```\nrequest → Uvicorn → FastAPI router → async handler (event loop)\n                                   or sync handler (thread pool)\n                                   → Pydantic serialize → response\n```\n\nRule of thumb: one worker process = one event loop = many concurrent requests,\nbut only one coroutine actually executes CPU instructions at a time.\n",{"id":91,"difficulty":34,"q":92,"a":93},"asyncio-task","What is `asyncio.create_task()` and how does it differ from `await`?","`asyncio.create_task(coro)` schedules a coroutine to run **concurrently** on the\nevent loop and returns a `Task` object immediately. The task starts running as\nsoon as the current coroutine yields. `await coro` runs the coroutine\n**sequentially** — the caller suspends until it finishes.\n\n```python\nasync def handler():\n    # sequential: total time = t_a + t_b\n    result_a = await slow_query_a()\n    result_b = await slow_query_b()\n\n    # concurrent: total time = max(t_a, t_b)\n    task_a = asyncio.create_task(slow_query_a())\n    task_b = asyncio.create_task(slow_query_b())\n    result_a = await task_a\n    result_b = await task_b\n```\n\n`asyncio.gather(*coros)` is usually more convenient than manual `create_task`\nwhen you want results; `create_task` is useful when you want to start a task\nand do other work before collecting its result.\n\nRule of thumb: `create_task` is fire-and-partially-forget; use `gather` when you\nneed all results in one call.\n",{"id":95,"difficulty":63,"q":96,"a":97},"thread-pool-size","How does FastAPI\u002Fanyio determine the size of its thread pool for sync handlers?","FastAPI delegates sync (`def`) handlers to **anyio**'s default thread limiter,\nwhich caps concurrent threads at **40 by default** (per worker process). This\nprevents unbounded thread creation from swamping the OS.\n\n```python\nimport anyio\n\n# Check or raise the cap at startup\n@app.on_event(\"startup\")\nasync def configure_threads():\n    limiter = anyio.from_thread.current_default_thread_limiter()\n    limiter.total_tokens = 20   # reduce if each thread uses lots of memory\n```\n\nThe 40-thread default is conservative for most workloads. If your sync handlers\ndo fast CPU work, 40 is plenty. If they block on slow external calls, consider\nusing `async def` + `asyncio.to_thread()` instead so you control the pool.\n\nRule of thumb: for heavily sync-bound FastAPI apps, tune the anyio thread limit or,\nbetter, switch to async libraries so you don't need threads at all.\n",{"id":99,"difficulty":34,"q":100,"a":101},"lifespan-startup-shutdown","What is the difference between `@app.on_event(\"startup\")` and `lifespan`?","`@app.on_event(\"startup\u002Fshutdown\")` decorators are the **legacy** approach —\nthey work but are deprecated in recent FastAPI. The modern replacement is the\n**`lifespan` context manager**, which groups startup and shutdown in one function\nusing `yield`.\n\n```python\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    # startup\n    await db_pool.connect()\n    yield\n    # shutdown\n    await db_pool.disconnect()\n\napp = FastAPI(lifespan=lifespan)\n```\n\nBenefits of `lifespan`:\n- Single function instead of two decorators.\n- Exception in startup cleanly skips the yield and shutdown block won't be reached.\n- Composable — you can call other async context managers inside.\n- Testable with `@asynccontextmanager` directly.\n\nRule of thumb: use `lifespan=` for all new FastAPI apps; avoid `@app.on_event`.\n",15,null,{"description":31},"FastAPI async\u002Fawait interview questions — coroutines, event loop, ASGI vs WSGI, sync vs async route handlers and avoiding blocking the event loop.","fastapi\u002Ffundamentals\u002Fasync-basics","2026-06-20","n2u_aF57BBwUZZehz50R-u1kpVdckwYf2HwJHCctb6c",{"id":110,"title":111,"body":112,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":116,"navigation":37,"order":32,"path":117,"questions":118,"questionsCount":175,"related":103,"seo":176,"seoDescription":177,"stem":178,"subtopic":111,"topic":19,"topicSlug":20,"updated":107,"__hash__":179},"qa\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle.md","Request Lifecycle",{"type":28,"value":113,"toc":114},[],{"title":31,"searchDepth":32,"depth":32,"links":115},[],{},"\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle",[119,123,127,131,135,139,143,147,151,155,159,163,167,171],{"id":120,"difficulty":34,"q":121,"a":122},"request-lifecycle-overview","Walk through the lifecycle of a FastAPI HTTP request from socket to response.","1. **Uvicorn** accepts the TCP connection and parses HTTP\u002F1.1 or HTTP\u002F2 bytes\n   into an ASGI `scope + receive\u002Fsend` pair.\n2. **Middleware stack** (outermost first) wraps the request. Each middleware can\n   short-circuit or modify `request` before passing to the next layer.\n3. **FastAPI router** matches the URL path + HTTP method to a route handler.\n   If no match → 404 `HTTPException`.\n4. **Dependency injection** resolves all `Depends()` in the handler signature,\n   recursively, before the handler runs.\n5. **Parameter extraction & Pydantic validation** — path params, query params,\n   headers, cookies and the request body are parsed and validated against type\n   annotations. Validation error → 422 `RequestValidationError`.\n6. **Handler executes** — `async def` on the event loop, `def` in a thread pool.\n7. **Response serialization** — the return value is filtered through\n   `response_model` (if set), then serialized to JSON via Pydantic.\n8. **Middleware stack** (innermost first on the way out) can modify the response.\n9. Uvicorn writes the HTTP response bytes to the socket.\n\nRule of thumb: middleware wraps everything; dependency injection happens before\nvalidation; validation happens before the handler body.\n",{"id":124,"difficulty":34,"q":125,"a":126},"middleware-order","In what order does FastAPI execute multiple middleware?","Middleware added with `app.add_middleware()` wraps the app **from the outside in**:\nthe last middleware added becomes the outermost layer. For requests, outermost\nruns first; for responses, innermost runs first (onion model).\n\n```python\napp.add_middleware(TimingMiddleware)   # added first → innermost layer\napp.add_middleware(AuthMiddleware)     # added second → outermost layer\n\n# Request flow:  AuthMiddleware → TimingMiddleware → route handler\n# Response flow: route handler → TimingMiddleware → AuthMiddleware\n```\n\nThis mirrors how ASGI middleware chains work in Starlette. FastAPI also applies\nits own built-in error handler and routing logic inside the inner layers.\n\nRule of thumb: add logging\u002Ftiming middleware last so it wraps everything,\nincluding auth middleware.\n",{"id":128,"difficulty":42,"q":129,"a":130},"http-exception","How does `HTTPException` work in FastAPI?","`HTTPException` is FastAPI's way to abort a request with a specific HTTP status\ncode and detail message. Raising it anywhere in a handler or dependency\nimmediately skips the rest of the stack and returns a JSON error response.\n\n```python\nfrom fastapi import HTTPException\n\n@app.get(\"\u002Fitems\u002F{item_id}\")\nasync def get_item(item_id: int):\n    item = await db.get(item_id)\n    if not item:\n        raise HTTPException(status_code=404, detail=\"Item not found\")\n    return item\n# Response: {\"detail\": \"Item not found\"} with HTTP 404\n```\n\nYou can add custom `headers` for auth challenges:\n```python\nraise HTTPException(\n    status_code=401,\n    detail=\"Not authenticated\",\n    headers={\"WWW-Authenticate\": \"Bearer\"},\n)\n```\n\nRule of thumb: raise `HTTPException` for expected error conditions\n(not found, unauthorized); let unhandled exceptions propagate to the\nglobal exception handler for unexpected errors.\n",{"id":132,"difficulty":34,"q":133,"a":134},"exception-handlers","How do you add a custom exception handler in FastAPI?","Use `@app.exception_handler(ExcType)` to register a handler that catches a\nspecific exception class anywhere in the request chain.\n\n```python\nfrom fastapi import Request\nfrom fastapi.responses import JSONResponse\n\nclass InsufficientFundsError(Exception):\n    def __init__(self, balance: float):\n        self.balance = balance\n\n@app.exception_handler(InsufficientFundsError)\nasync def funds_handler(request: Request, exc: InsufficientFundsError):\n    return JSONResponse(\n        status_code=402,\n        content={\"detail\": f\"Balance too low: {exc.balance}\"},\n    )\n```\n\nTo override the default validation error handler:\n```python\nfrom fastapi.exceptions import RequestValidationError\n\n@app.exception_handler(RequestValidationError)\nasync def validation_handler(request: Request, exc: RequestValidationError):\n    return JSONResponse(status_code=422, content={\"errors\": exc.errors()})\n```\n\nRule of thumb: use `HTTPException` for expected HTTP errors; use custom exception\nhandlers to convert domain exceptions into HTTP responses without polluting handlers.\n",{"id":136,"difficulty":42,"q":137,"a":138},"request-validation-error","What response does FastAPI return when request validation fails?","FastAPI returns **HTTP 422 Unprocessable Entity** with a JSON body that lists\nevery field that failed validation. This is automatic — you don't write any\nvalidation code yourself.\n\n```json\n{\n  \"detail\": [\n    {\n      \"type\": \"missing\",\n      \"loc\": [\"body\", \"email\"],\n      \"msg\": \"Field required\",\n      \"input\": {\"name\": \"Alice\"},\n      \"url\": \"https:\u002F\u002Ferrors.pydantic.dev\u002F2.0\u002Fv\u002Fmissing\"\n    }\n  ]\n}\n```\n\nThe `loc` array pinpoints where the bad value came from: `[\"body\", \"field\"]`\nfor body params, `[\"query\", \"name\"]` for query params, `[\"path\", \"id\"]` for\npath params.\n\nRule of thumb: 422 = you sent bad data; 400 = request was intentionally rejected\nby application logic; treat them differently in client error handling.\n",{"id":140,"difficulty":63,"q":141,"a":142},"dependency-resolution-order","In what order does FastAPI resolve dependencies, and what happens if two dependencies share a sub-dependency?","FastAPI builds a **dependency graph** at startup by inspecting type annotations\nof all handlers. At request time it resolves **depth-first**: sub-dependencies\nare resolved before the dependencies that need them.\n\n**Shared dependencies are called once per request** (by default). If two\ndependencies both declare `Depends(get_db)`, FastAPI calls `get_db()` once and\npasses the same object to both.\n\n```python\nasync def get_db():        # called once even if used by two deps\n    async with AsyncSession() as session:\n        yield session\n\nasync def get_current_user(db = Depends(get_db)):\n    ...\n\nasync def check_permission(db = Depends(get_db)):\n    ...\n\n@app.get(\"\u002Fsecure\")\nasync def handler(\n    user = Depends(get_current_user),\n    perm = Depends(check_permission),  # same db session as above\n):\n    ...\n```\n\nTo opt out of caching (force a fresh call), pass `use_cache=False`:\n`Depends(get_db, use_cache=False)`.\n\nRule of thumb: shared sub-dependencies are singletons per request — rely on this\nto share a DB session cleanly without a thread-local or global.\n",{"id":144,"difficulty":34,"q":145,"a":146},"response-serialization","How does FastAPI serialize the value returned from a route handler?","1. If a `response_model` is set, FastAPI uses Pydantic to validate and filter\n   the return value through that model (strips extra fields, applies aliases, etc.).\n2. The filtered Python object is converted to a JSON-compatible dict via\n   Pydantic's `model_dump(mode=\"json\")`.\n3. The dict is serialized to JSON bytes using `orjson` (if installed) or the\n   standard `json` module.\n4. The bytes are sent as `Content-Type: application\u002Fjson`.\n\n```python\nclass UserOut(BaseModel):\n    id: int\n    name: str\n    # no `password` field → never leaked\n\n@app.get(\"\u002Fusers\u002F{id}\", response_model=UserOut)\nasync def get_user(id: int) -> User:\n    return await db.get(User, id)   # User has password field — filtered out\n```\n\nIf no `response_model` is set, FastAPI calls `jsonable_encoder()` on the return\nvalue, which handles datetime, UUID, Enum etc.\n\nRule of thumb: always set `response_model` on endpoints that return ORM objects\nto prevent accidental data leakage.\n",{"id":148,"difficulty":34,"q":149,"a":150},"background-task-in-lifecycle","Where in the request lifecycle do BackgroundTasks run?","`BackgroundTasks` run **after the HTTP response has been sent** to the client.\nThe route handler adds tasks; FastAPI sends the response; then the tasks execute\nsequentially in the same event loop (for async tasks) or thread pool (for sync tasks).\n\n```python\nfrom fastapi import BackgroundTasks\n\ndef send_email(to: str, msg: str):\n    ...   # slow SMTP call — runs after response is sent\n\n@app.post(\"\u002Fsignup\")\nasync def signup(email: str, background_tasks: BackgroundTasks):\n    await db.create_user(email)\n    background_tasks.add_task(send_email, email, \"Welcome!\")\n    return {\"status\": \"created\"}   # response sent immediately\n```\n\nBecause they run in the same process, background tasks share memory with the app\nbut have **no access** to the request object once it's closed.\n\nRule of thumb: use `BackgroundTasks` for quick fire-and-forget work (email,\nanalytics); use a dedicated task queue (Celery, ARQ) for retryable or long-running jobs.\n",{"id":152,"difficulty":34,"q":153,"a":154},"jsonable-encoder","What is `jsonable_encoder` and when do you need it?","`jsonable_encoder` converts Python objects that aren't natively JSON-serialisable\n(datetime, UUID, Decimal, Pydantic models, ORM objects) into JSON-compatible\nPython primitives (str, int, dict, list).\n\n```python\nfrom fastapi.encoders import jsonable_encoder\nfrom datetime import datetime\n\nobj = {\"created\": datetime(2026, 1, 1), \"id\": uuid4()}\nsafe = jsonable_encoder(obj)\n# {\"created\": \"2026-01-01T00:00:00\", \"id\": \"550e8400-...\"}\n```\n\nFastAPI calls it automatically when serializing responses. You need to call it\nmanually when:\n- Storing something in a cache or NoSQL DB as JSON.\n- Passing an object to a `JSONResponse` constructor directly.\n\n```python\nreturn JSONResponse(content=jsonable_encoder(my_pydantic_model))\n```\n\nRule of thumb: let FastAPI call `jsonable_encoder` implicitly through `response_model`;\ncall it explicitly only when building `JSONResponse` objects manually.\n",{"id":156,"difficulty":42,"q":157,"a":158},"request-body-parsing","How does FastAPI decide where to look for a parameter — path, query, or body?","FastAPI uses the **parameter name and type annotation** as signals:\n\n| Source | Signal |\n|--------|--------|\n| Path parameter | name appears in the route path string `\u002F{name}` |\n| Query parameter | simple type (`str`, `int`, `float`, `bool`, `Optional`) not in path |\n| Request body | Pydantic `BaseModel` subclass (or annotated with `Body()`) |\n| Header | annotated with `Header()` |\n| Cookie | annotated with `Cookie()` |\n\n```python\n@app.put(\"\u002Fitems\u002F{item_id}\")\nasync def update_item(\n    item_id: int,          # path  — in \"{item_id}\"\n    q: str | None = None,  # query — simple type, not in path\n    item: Item,            # body  — Pydantic model\n):\n    ...\n```\n\nRule of thumb: if the name matches the path template, it's a path param; if it's\na Pydantic model it's a body; everything else defaults to query.\n",{"id":160,"difficulty":42,"q":161,"a":162},"response-status-code","How do you change the default response status code in FastAPI?","Pass `status_code` to the route decorator. FastAPI returns 200 by default for GET\nand 200 for POST; 201 is the semantic choice for resource creation.\n\n```python\nfrom fastapi import status\n\n@app.post(\"\u002Fitems\", status_code=status.HTTP_201_CREATED)\nasync def create_item(item: Item):\n    saved = await db.save(item)\n    return saved\n```\n\nTo set the status code dynamically inside the handler, inject the `Response` object:\n\n```python\nfrom fastapi import Response\n\n@app.get(\"\u002Fmaybe\")\nasync def maybe(response: Response, id: int):\n    item = await db.get(id)\n    if not item:\n        response.status_code = 404\n        return None\n    return item\n```\n\nRule of thumb: set status codes at the decorator level for fixed codes;\ninject `Response` only when the code depends on runtime logic.\n",{"id":164,"difficulty":34,"q":165,"a":166},"path-vs-router-prefix","What is the difference between setting a path in `app.include_router(prefix=...)` vs the route decorator itself?","`prefix` in `include_router` is a **path segment prepended to all routes** in\nthat router. The route decorator path is the **specific endpoint path within the\nrouter**. They concatenate.\n\n```python\nrouter = APIRouter()\n\n@router.get(\"\u002Fitems\")           # partial path\nasync def list_items(): ...\n\n@router.get(\"\u002Fitems\u002F{id}\")      # partial path\nasync def get_item(id: int): ...\n\napp.include_router(router, prefix=\"\u002Fv1\")\n# registered as: GET \u002Fv1\u002Fitems  and  GET \u002Fv1\u002Fitems\u002F{id}\n```\n\nSetting the full path in the decorator makes the router useless as a module\nprefix. Putting everything in `prefix` lets you re-mount the same router at\ndifferent prefixes (e.g., `\u002Fv1` and `\u002Fv2`).\n\nRule of thumb: keep route decorators as relative sub-paths; use `prefix` in\n`include_router` for versioning or top-level resource grouping.\n",{"id":168,"difficulty":42,"q":169,"a":170},"trailing-slash","Does FastAPI treat `\u002Fitems` and `\u002Fitems\u002F` as the same route?","**No.** By default FastAPI treats trailing slashes as distinct routes and does\n**not** redirect. A GET to `\u002Fitems\u002F` returns 404 if only `\u002Fitems` is registered.\n\n```python\n@app.get(\"\u002Fitems\")     # only matches \u002Fitems\nasync def list_items(): ...\n```\n\nOptions:\n1. Register both: `@app.get(\"\u002Fitems\")` and `@app.get(\"\u002Fitems\u002F\")`.\n2. Use the `redirect_slashes=False` or `redirect_slashes=True` flag on `FastAPI()`:\n   `app = FastAPI(redirect_slashes=True)` — a trailing-slash request is 307-redirected\n   to the non-slash version.\n\n```python\napp = FastAPI(redirect_slashes=True)\n```\n\nRule of thumb: disable automatic trailing-slash handling (`redirect_slashes=False`)\nfor strict APIs; enable it (`True`) for user-facing endpoints where clients vary.\n",{"id":172,"difficulty":34,"q":173,"a":174},"openapi-schema-generation","When is the OpenAPI schema generated and can it be disabled?","FastAPI generates the OpenAPI schema **at import\u002Fstartup time**, not per-request.\nIt inspects route decorators, type annotations and Pydantic models once and caches\nthe result. The `\u002Fopenapi.json` endpoint just serves this cached dict.\n\nTo disable the docs entirely (e.g., in production):\n\n```python\napp = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)\n```\n\nTo move the schema to a non-default path:\n```python\napp = FastAPI(openapi_url=\"\u002Fapi\u002Fv1\u002Fopenapi.json\")\n```\n\nLazy generation (compute only on first request) can be achieved by subclassing:\n```python\nclass LazyFastAPI(FastAPI):\n    def openapi(self):\n        if not self.openapi_schema:\n            self.openapi_schema = get_openapi(...)\n        return self.openapi_schema\n```\n\nRule of thumb: disable `openapi_url` in production if you don't want to expose\nyour API schema publicly; keep it enabled in staging for team tooling.\n",14,{"description":31},"FastAPI request lifecycle interview questions — middleware chain, routing, dependency resolution, validation, handler execution and response serialization.","fastapi\u002Ffundamentals\u002Frequest-lifecycle","FiKs69biTzPCIi3Wz4Yb1o_O1sXxKJrz07q25xL6GBg",{"id":181,"title":182,"body":183,"description":31,"difficulty":42,"extension":35,"framework":10,"frameworkSlug":8,"meta":187,"navigation":37,"order":188,"path":189,"questions":190,"questionsCount":102,"related":103,"seo":251,"seoDescription":252,"stem":253,"subtopic":182,"topic":19,"topicSlug":20,"updated":107,"__hash__":254},"qa\u002Ffastapi\u002Ffundamentals\u002Fpath-operations.md","Path Operations",{"type":28,"value":184,"toc":185},[],{"title":31,"searchDepth":32,"depth":32,"links":186},[],{},3,"\u002Ffastapi\u002Ffundamentals\u002Fpath-operations",[191,195,199,203,207,211,215,219,223,227,231,235,239,243,247],{"id":192,"difficulty":42,"q":193,"a":194},"what-is-path-operation","What is a \"path operation\" in FastAPI?","A **path operation** is the combination of an HTTP method and a URL path bound\nto a Python function via a decorator. FastAPI registers it as a route and\nauto-generates the OpenAPI schema entry for it.\n\n```python\n@app.get(\"\u002Fitems\")           # GET  \u002Fitems  → list_items\nasync def list_items():\n    return [{\"id\": 1}]\n\n@app.post(\"\u002Fitems\")          # POST \u002Fitems  → create_item\nasync def create_item(item: Item):\n    return item\n```\n\nEach decorator (`@app.get`, `@app.post`, `@app.put`, `@app.patch`,\n`@app.delete`, `@app.head`, `@app.options`, `@app.trace`) corresponds to\nan HTTP method. The same path can be registered for multiple methods.\n\nRule of thumb: one decorator = one path operation = one OpenAPI endpoint entry.\n",{"id":196,"difficulty":42,"q":197,"a":198},"http-methods","Which HTTP methods does FastAPI support as route decorators?","FastAPI mirrors standard HTTP methods:\n\n| Decorator | Method | Typical use |\n|-----------|--------|-------------|\n| `@app.get` | GET | Read resource |\n| `@app.post` | POST | Create resource |\n| `@app.put` | PUT | Full update\u002Freplace |\n| `@app.patch` | PATCH | Partial update |\n| `@app.delete` | DELETE | Remove resource |\n| `@app.head` | HEAD | Like GET but no body |\n| `@app.options` | OPTIONS | CORS preflight \u002F introspection |\n| `@app.trace` | TRACE | Diagnostic loopback |\n\n```python\n@app.patch(\"\u002Fitems\u002F{id}\")\nasync def update_partial(id: int, patch: ItemPatch):\n    ...\n```\n\nRule of thumb: use PUT for full replacement, PATCH for partial updates — FastAPI\ndoesn't enforce semantics, but your OpenAPI schema and clients will reflect the choice.\n",{"id":200,"difficulty":34,"q":201,"a":202},"operation-id","What is an `operation_id` and why does it matter?","The `operation_id` is a **unique string identifier** for an OpenAPI operation.\nCode generators (openapi-generator, orval) use it as the function name in\ngenerated clients. FastAPI auto-generates it from the handler function name\nand the path, but you can override it.\n\n```python\n@app.get(\n    \"\u002Fitems\u002F{item_id}\",\n    operation_id=\"retrieve_item\",   # generated client function: retrieve_item()\n)\nasync def get_item(item_id: int):\n    ...\n```\n\nWithout an explicit `operation_id`, FastAPI generates something like\n`get_item_items__item_id__get` — verbose and brittle across renames.\n\nRule of thumb: set explicit `operation_id` values for any endpoint consumed by\ngenerated client code to keep client method names stable across refactors.\n",{"id":204,"difficulty":42,"q":205,"a":206},"tags-and-grouping","How do `tags` work in FastAPI route decorators?","`tags` is a list of strings that group related operations in the Swagger UI and\nReDoc docs. They have no effect on routing — they're purely for documentation.\n\n```python\n@app.get(\"\u002Fusers\", tags=[\"users\"])\nasync def list_users(): ...\n\n@app.post(\"\u002Fusers\", tags=[\"users\"])\nasync def create_user(): ...\n\n@app.get(\"\u002Forders\", tags=[\"orders\"])\nasync def list_orders(): ...\n```\n\nYou can also set tags at the `APIRouter` level so every route in the router\ninherits them:\n\n```python\nrouter = APIRouter(prefix=\"\u002Fusers\", tags=[\"users\"])\napp.include_router(router)\n```\n\nRule of thumb: set tags at the router level for consistency; override per-route\nonly when a route genuinely belongs to a different OpenAPI group.\n",{"id":208,"difficulty":42,"q":209,"a":210},"deprecated-routes","How do you mark a route as deprecated in FastAPI?","Pass `deprecated=True` to the route decorator. FastAPI adds the OpenAPI\n`deprecated: true` flag, which Swagger UI displays with a strikethrough.\n\n```python\n@app.get(\"\u002Fold-endpoint\", deprecated=True)\nasync def old_endpoint():\n    return RedirectResponse(\"\u002Fnew-endpoint\", status_code=301)\n```\n\nDeprecating in OpenAPI doesn't prevent the route from being called — you still\nneed to keep the handler working. For hard removal, emit a `Deprecation` header\nwith a sunset date, then delete the route in a future release.\n\nRule of thumb: always mark deprecated routes in OpenAPI before removing them\nso generated clients surface the warning during their build step.\n",{"id":212,"difficulty":42,"q":213,"a":214},"response-description","How do you document response codes and descriptions in FastAPI?","Use `responses` dict in the route decorator to document non-default status codes.\nThe `summary` and `description` parameters document the operation itself.\n\n```python\n@app.get(\n    \"\u002Fitems\u002F{id}\",\n    summary=\"Retrieve an item\",\n    description=\"Returns the full item record. Returns 404 if the item does not exist.\",\n    responses={\n        404: {\"description\": \"Item not found\"},\n        200: {\"description\": \"The item\", \"model\": Item},\n    },\n)\nasync def get_item(id: int):\n    ...\n```\n\nFastAPI also reads the handler's docstring as the operation description:\n\n```python\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int):\n    \"\"\"Return a single item by its numeric ID.\"\"\"\n    ...\n```\n\nRule of thumb: use the docstring for prose descriptions; use the `responses` dict\nto document error codes and their schemas.\n",{"id":216,"difficulty":34,"q":217,"a":218},"multiple-responses","How do you declare that a route can return different response models for different status codes?","Use the `responses` parameter with a dict keyed by status code. For typed bodies\npass `\"model\": PydanticModel`.\n\n```python\nfrom fastapi.responses import JSONResponse\nfrom typing import Union\n\nclass Item(BaseModel): id: int; name: str\nclass Message(BaseModel): message: str\n\n@app.get(\n    \"\u002Fitems\u002F{id}\",\n    response_model=Item,                      # 200 default\n    responses={404: {\"model\": Message}},      # 404 documented\n)\nasync def get_item(id: int) -> Union[Item, JSONResponse]:\n    item = await db.get(id)\n    if not item:\n        return JSONResponse(\n            status_code=404,\n            content={\"message\": \"not found\"},\n        )\n    return item\n```\n\nNote: FastAPI only **validates** the `response_model`; additional entries in\n`responses` are documentation-only.\n\nRule of thumb: document all realistic non-200 responses in `responses={}` even if\nFastAPI won't validate them — clients and SDK generators rely on the schema.\n",{"id":220,"difficulty":34,"q":221,"a":222},"path-order-matters","Why does route order matter in FastAPI, and what is the common pitfall?","FastAPI matches routes **in the order they are registered**. The first matching\nroute wins. If a fixed path like `\u002Fitems\u002Fexport` is registered after a\nparameterised path like `\u002Fitems\u002F{id}`, the `{id}` route captures the string\n`\"export\"` as the value of `id`.\n\n```python\n# WRONG order — \u002Fitems\u002Fexport matches the first route with id=\"export\"\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int): ...   # FastAPI raises 422 because \"export\" is not int\n\n@app.get(\"\u002Fitems\u002Fexport\")\nasync def export_items(): ...      # never reached\n\n# CORRECT order — fixed paths before parameterised\n@app.get(\"\u002Fitems\u002Fexport\")\nasync def export_items(): ...\n\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int): ...\n```\n\nRule of thumb: register fixed\u002Fstatic paths before parameterised ones at the same\ndepth — or use type constraints (`{id:int}`) to let FastAPI discriminate automatically.\n",{"id":224,"difficulty":34,"q":225,"a":226},"path-converters","How do you constrain a path parameter to a specific type in the URL pattern?","FastAPI uses Starlette's **path converters** in the URL string to constrain path\nparameters without Pydantic at the routing layer:\n\n```python\n@app.get(\"\u002Fitems\u002F{item_id:int}\")    # only matches if segment is an integer\nasync def get_item(item_id: int): ...\n\n@app.get(\"\u002Ffiles\u002F{file_path:path}\") # matches any path, including slashes\nasync def get_file(file_path: str): ...\n```\n\nConverters: `str` (default), `int`, `float`, `uuid`, `path`.\n\nCombining with Pydantic type annotations provides two-level validation:\nStarlette rejects non-int URLs at routing time; Pydantic further validates range\nor constraints in the handler.\n\nRule of thumb: use `:int` or `:uuid` converters in paths to prevent wrong-type\nsegments from even reaching the handler — cleaner 404 instead of 422.\n",{"id":228,"difficulty":34,"q":229,"a":230},"route-include-router","What is `APIRouter` and why should you use it instead of `app` directly?","`APIRouter` is FastAPI's mechanism for splitting route definitions across multiple\nfiles. Routes are registered on the router, then the router is mounted onto the\napp (or another router) with a prefix and shared config.\n\n```python\n# routers\u002Fusers.py\nfrom fastapi import APIRouter\nrouter = APIRouter(prefix=\"\u002Fusers\", tags=[\"users\"])\n\n@router.get(\"\u002F\")\nasync def list_users(): ...\n\n@router.get(\"\u002F{id}\")\nasync def get_user(id: int): ...\n```\n\n```python\n# main.py\nfrom routers import users\napp.include_router(users.router)\n# registers: GET \u002Fusers\u002F  and  GET \u002Fusers\u002F{id}\n```\n\nBenefits: logical grouping, reusable prefix\u002Ftags\u002Fdependencies, testable in\nisolation, avoids a 1000-line main.py.\n\nRule of thumb: one `APIRouter` per resource or domain concept; mount all routers\nin `main.py`.\n",{"id":232,"difficulty":34,"q":233,"a":234},"include-router-dependencies","How do you apply a dependency to every route in an `APIRouter`?","Pass `dependencies=[Depends(fn)]` to `APIRouter()` or `include_router()`. Every\nroute in that router runs the dependency before the handler — useful for auth or\nrate-limiting.\n\n```python\nfrom fastapi import APIRouter, Depends\nfrom .auth import verify_token\n\n# Option A: on the router itself\nrouter = APIRouter(\n    prefix=\"\u002Fadmin\",\n    dependencies=[Depends(verify_token)],\n)\n\n# Option B: at mount time (non-destructive)\napp.include_router(router, dependencies=[Depends(verify_token)])\n```\n\nBoth options stack — if you set deps in both places, both run.\nRouter-level deps run before route-level deps.\n\nRule of thumb: put auth\u002Frate-limiting deps at the router level so you can't\naccidentally forget them on a new route; put business-logic deps at the route level.\n",{"id":236,"difficulty":34,"q":237,"a":238},"return-type-annotation","What is the effect of adding a return type annotation to a FastAPI handler?","FastAPI uses the return type annotation in two ways:\n1. **OpenAPI schema** — the annotation becomes the documented response schema.\n2. **Response validation** — if `response_model` is also set, it overrides; if not,\n   FastAPI uses the return annotation as the implicit `response_model`.\n\n```python\nclass UserOut(BaseModel):\n    id: int\n    name: str\n\n@app.get(\"\u002Fusers\u002F{id}\")\nasync def get_user(id: int) -> UserOut:   # annotation drives the schema\n    return await db.get(User, id)\n```\n\nSince Pydantic v2 + FastAPI 0.100+, you can return an ORM object that matches\n`UserOut` and FastAPI will serialise it. Using `response_model=UserOut` in the\ndecorator is equivalent but takes precedence.\n\nRule of thumb: use return type annotations as the primary way to document response\nschemas — it's more Pythonic and keeps the decorator line shorter.\n",{"id":240,"difficulty":42,"q":241,"a":242},"no-content-response","How do you return a 204 No Content response from FastAPI?","Set `status_code=204` in the decorator and return `None` (or nothing). FastAPI\nsends no body for 204 responses.\n\n```python\nfrom fastapi import status\nfrom fastapi.responses import Response\n\n@app.delete(\"\u002Fitems\u002F{id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_item(id: int):\n    await db.delete(id)\n    # implicit return None → no body sent\n```\n\nAlternatively, return `Response(status_code=204)` explicitly when you need to\navoid FastAPI trying to serialise `None`.\n\nRule of thumb: always use 204 for successful DELETE operations that return no\nbody — clients rely on the status code to know there's nothing to parse.\n",{"id":244,"difficulty":34,"q":245,"a":246},"websocket-route","How do you define a WebSocket route in FastAPI?","Use `@app.websocket(\"\u002Fpath\")` and declare the handler with a `WebSocket`\nparameter. The handler stays alive in a loop, sending and receiving messages\nuntil the connection closes.\n\n```python\nfrom fastapi import WebSocket\n\n@app.websocket(\"\u002Fws\")\nasync def websocket_endpoint(ws: WebSocket):\n    await ws.accept()\n    try:\n        while True:\n            data = await ws.receive_text()\n            await ws.send_text(f\"Echo: {data}\")\n    except WebSocketDisconnect:\n        pass   # client closed the connection\n```\n\nWebSocket handlers can use `Depends()` just like HTTP handlers, making it easy\nto authenticate the upgrade request before `ws.accept()`.\n\nRule of thumb: always call `await ws.accept()` before sending; catch\n`WebSocketDisconnect` to clean up resources when the client leaves.\n",{"id":248,"difficulty":42,"q":249,"a":250},"static-files","How do you serve static files in FastAPI?","Mount Starlette's `StaticFiles` on a path:\n\n```python\nfrom fastapi.staticfiles import StaticFiles\n\napp.mount(\"\u002Fstatic\", StaticFiles(directory=\"static\"), name=\"static\")\n```\n\nRequests to `\u002Fstatic\u002Flogo.png` serve `.\u002Fstatic\u002Flogo.png` directly without going\nthrough FastAPI's routing. It's handled at the ASGI mount level, so it bypasses\nmiddleware that is added _after_ the mount.\n\nFor a single-page app (SPA) fallback:\n```python\napp.mount(\"\u002F\", StaticFiles(directory=\"dist\", html=True), name=\"spa\")\n```\n`html=True` serves `index.html` for any path not found in the directory.\n\nRule of thumb: use `StaticFiles` for assets; for dynamically generated file\ndownloads use `FileResponse` inside a regular route handler.\n",{"description":31},"FastAPI path operations interview questions — decorators, HTTP methods, status codes, operation metadata, tags, deprecated routes and include_router.","fastapi\u002Ffundamentals\u002Fpath-operations","9QLxXiT9OhguZvSQ91Ol9hHDan8TJJBNIKJvImgvmSc",{"id":256,"title":257,"body":258,"description":31,"difficulty":42,"extension":35,"framework":10,"frameworkSlug":8,"meta":262,"navigation":37,"order":263,"path":264,"questions":265,"questionsCount":175,"related":103,"seo":322,"seoDescription":323,"stem":324,"subtopic":325,"topic":19,"topicSlug":20,"updated":107,"__hash__":326},"qa\u002Ffastapi\u002Ffundamentals\u002Ftype-hints.md","Type Hints",{"type":28,"value":259,"toc":260},[],{"title":31,"searchDepth":32,"depth":32,"links":261},[],{},4,"\u002Ffastapi\u002Ffundamentals\u002Ftype-hints",[266,270,274,278,282,286,290,294,298,302,306,310,314,318],{"id":267,"difficulty":42,"q":268,"a":269},"why-type-hints","Why are Python type hints central to how FastAPI works?","FastAPI uses **runtime type inspection** (via `typing`, `inspect` and Pydantic)\nto derive three things from your function signatures automatically:\n\n1. **Where** each parameter comes from (path, query, body, header).\n2. **How** to parse and validate the incoming value (int, str, Pydantic model).\n3. **What** the OpenAPI schema should look like.\n\n```python\n@app.get(\"\u002Fitems\u002F{item_id}\")\nasync def get_item(\n    item_id: int,          # → path int, validated, documented as integer\n    q: str | None = None,  # → optional query string, documented\n    item: Item,            # → JSON body parsed into Pydantic model\n):\n    ...\n```\n\nWithout type hints, FastAPI cannot infer any of this — parameters become\nuntyped strings and no schema is generated.\n\nRule of thumb: in FastAPI, type annotations are not optional documentation —\nthey are the mechanism that drives parsing, validation, and the API contract.\n",{"id":271,"difficulty":42,"q":272,"a":273},"optional-parameters","How do you declare an optional query parameter in FastAPI?","Give the parameter a default value of `None` and annotate it with\n`str | None` (Python 3.10+) or `Optional[str]` (Python 3.9-).\n\n```python\nfrom typing import Optional\n\n@app.get(\"\u002Fitems\")\nasync def list_items(\n    q: str | None = None,          # optional, defaults to None\n    limit: int = 10,               # optional with a non-None default\n    offset: int = 0,\n):\n    ...\n```\n\nIn the OpenAPI schema, parameters with defaults appear as `required: false`.\nFastAPI only marks a query param `required: true` when there is **no default**.\n\nRule of thumb: `= None` makes any parameter optional; omit the default to make\nit required.\n",{"id":275,"difficulty":34,"q":276,"a":277},"annotated","What is `Annotated` and how does FastAPI use it?","`Annotated[T, metadata]` (from `typing`) attaches extra metadata to a type\nwithout changing the type itself. FastAPI reads the metadata to configure\nparameter behaviour — Query constraints, Body options, Depends() etc.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query, Path\n\n@app.get(\"\u002Fitems\u002F{item_id}\")\nasync def get_item(\n    item_id: Annotated[int, Path(ge=1)],          # path param, must be >= 1\n    q: Annotated[str | None, Query(max_length=50)] = None,\n):\n    ...\n```\n\nThis keeps type information separate from validation metadata and works better\nwith type checkers (mypy, pyright) than the old `item_id: int = Path(ge=1)` style.\n\nRule of thumb: prefer `Annotated[T, Field(...)]` style in new code — it separates\nthe type from the constraint and is more explicit about what each annotation means.\n",{"id":279,"difficulty":34,"q":280,"a":281},"enum-annotation","How do you restrict a parameter to a fixed set of values using an Enum?","Annotate the parameter with a `str` (or `int`) Enum subclass. FastAPI validates\nthe incoming value against the enum members and documents the allowed values in\nOpenAPI.\n\n```python\nfrom enum import Enum\n\nclass Size(str, Enum):\n    small = \"small\"\n    medium = \"medium\"\n    large = \"large\"\n\n@app.get(\"\u002Fitems\")\nasync def list_items(size: Size = Size.medium):\n    return {\"size\": size.value}\n```\n\nInheriting from both `str` and `Enum` ensures the value is JSON-serialisable\nas a string directly.\n\nRule of thumb: use `str` enums for path\u002Fquery params, `int` enums for numeric\ncodes; always inherit from `str`\u002F`int` so serialisation is automatic.\n",{"id":283,"difficulty":34,"q":284,"a":285},"list-query-param","How do you accept a list of values from a query parameter?","Annotate with `list[str]` (or `List[str]`) and use `Query()` to tell FastAPI\nto collect multiple values for the same key.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\n@app.get(\"\u002Fitems\")\nasync def list_items(\n    tags: Annotated[list[str], Query()] = [],\n):\n    return {\"tags\": tags}\n# GET \u002Fitems?tags=python&tags=fastapi → {\"tags\": [\"python\", \"fastapi\"]}\n```\n\nWithout `Query()`, a bare `list[str]` annotation would be treated as a body\nparameter by FastAPI.\n\nRule of thumb: always wrap `list[T]` query params with `Query()` to signal that\nmultiple same-key values should be collected into a list.\n",{"id":287,"difficulty":42,"q":288,"a":289},"header-parameter","How do you read a request header in FastAPI?","Annotate the parameter with `Header()`. FastAPI automatically converts the\nparameter name from snake_case to the HTTP header's hyphen-case format.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Header\n\n@app.get(\"\u002Fitems\")\nasync def list_items(\n    x_token: Annotated[str | None, Header()] = None,\n):\n    # reads the \"X-Token\" HTTP header\n    return {\"token\": x_token}\n```\n\nTo read a header with underscores in the name (non-standard), pass\n`convert_underscores=False` to `Header()`.\n\nRule of thumb: FastAPI converts `_` → `-` automatically for headers; use\n`convert_underscores=False` only for custom non-standard header names.\n",{"id":291,"difficulty":42,"q":292,"a":293},"cookie-parameter","How do you read a cookie value in a FastAPI handler?","Annotate the parameter with `Cookie()`.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Cookie\n\n@app.get(\"\u002Fme\")\nasync def current_user(\n    session_id: Annotated[str | None, Cookie()] = None,\n):\n    if not session_id:\n        raise HTTPException(status_code=401)\n    return {\"session\": session_id}\n```\n\nCookies are accessible only via `Cookie()` — they won't be picked up as query\nor path params. To set a cookie in the response, inject `Response` and call\n`response.set_cookie(key, value)`.\n\nRule of thumb: read cookies with `Cookie()`, write them with `response.set_cookie()`\ninjected via the `Response` parameter.\n",{"id":295,"difficulty":34,"q":296,"a":297},"pydantic-field-in-handler","How do you add validation constraints (min\u002Fmax, regex) to a query parameter?","Use `Query()` with constraint kwargs inside `Annotated`. These map to Pydantic\nfield constraints under the hood.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\n@app.get(\"\u002Fitems\")\nasync def search(\n    q: Annotated[str, Query(min_length=3, max_length=100, pattern=r\"^\\w+$\")],\n    page: Annotated[int, Query(ge=1, le=1000)] = 1,\n):\n    ...\n```\n\nConstraints are reflected in the OpenAPI schema so clients can validate before\nsending.\n\nRule of thumb: put constraint kwargs in `Query()`\u002F`Path()`\u002F`Body()` — never\nvalidate manually in the handler body for data that comes from the request.\n",{"id":299,"difficulty":34,"q":300,"a":301},"body-singular-field","How do you accept a single non-model value in the request body?","Use `Body()` annotation. Without it, FastAPI treats simple types as query params.\n\n```python\nfrom typing import Annotated\nfrom fastapi import Body\n\n@app.post(\"\u002Fitems\u002F{id}\u002Ftag\")\nasync def add_tag(\n    id: int,\n    tag: Annotated[str, Body()],   # reads {\"tag\": \"python\"} or just \"python\" with embed=False\n):\n    ...\n```\n\nFor a single-field body that must be wrapped in a key:\n```python\ntag: Annotated[str, Body(embed=True)]   # expects {\"tag\": \"value\"}\n```\n\nRule of thumb: use a Pydantic model whenever you have multiple body fields;\nuse `Body()` only for a single primitive value that doesn't warrant a model.\n",{"id":303,"difficulty":34,"q":304,"a":305},"form-data","How do you accept form data (not JSON) in FastAPI?","Use `Form()` instead of `Body()`. Install `python-multipart` first.\n\n```python\nfrom fastapi import Form\n\n@app.post(\"\u002Flogin\")\nasync def login(\n    username: Annotated[str, Form()],\n    password: Annotated[str, Form()],\n):\n    ...\n```\n\nNote: **you cannot mix JSON body and `Form()` fields in the same handler** —\nthe `Content-Type` is either `application\u002Fjson` or `application\u002Fx-www-form-urlencoded`\u002F\n`multipart\u002Fform-data`, not both.\n\nRule of thumb: use `Form()` for HTML form submissions or OAuth2 password flows;\nuse JSON body for REST API calls.\n",{"id":307,"difficulty":34,"q":308,"a":309},"file-upload","How do you accept a file upload in FastAPI?","Use `UploadFile` for streaming (recommended) or `bytes` for small in-memory files.\n\n```python\nfrom fastapi import UploadFile, File\n\n@app.post(\"\u002Fupload\")\nasync def upload(file: UploadFile):\n    content = await file.read()\n    return {\"filename\": file.filename, \"size\": len(content)}\n```\n\n`UploadFile` attributes: `filename`, `content_type`, `file` (SpooledTemporaryFile).\nFor multiple files: `files: list[UploadFile]`.\n\nAlways `await file.read()` — the file object is async. For large files, read in\nchunks to avoid loading the whole thing into memory:\n\n```python\nwhile chunk := await file.read(8192):\n    process(chunk)\n```\n\nRule of thumb: prefer `UploadFile` over `bytes` — it streams large files without\nloading them into memory; `bytes` is fine only for small known-size uploads.\n",{"id":311,"difficulty":34,"q":312,"a":313},"response-model-exclude-none","How do you exclude `None` fields from a response in FastAPI?","Set `response_model_exclude_none=True` in the route decorator. FastAPI calls\n`model_dump(exclude_none=True)` on the Pydantic response model before serialising.\n\n```python\nclass UserOut(BaseModel):\n    id: int\n    name: str\n    bio: str | None = None\n\n@app.get(\"\u002Fusers\u002F{id}\", response_model=UserOut, response_model_exclude_none=True)\nasync def get_user(id: int):\n    return {\"id\": 1, \"name\": \"Alice\"}\n    # bio is None → omitted from response: {\"id\": 1, \"name\": \"Alice\"}\n```\n\nRelated flags: `response_model_exclude_unset=True` (skip fields not explicitly set\nby the handler) and `response_model_exclude={\"internal_field\"}` (always strip specific fields).\n\nRule of thumb: use `exclude_none=True` for sparse models where `None` means\n\"not present\"; use `exclude_unset=True` when you want PATCH-style partial output.\n",{"id":315,"difficulty":63,"q":316,"a":317},"union-response","How do you type a route that can return one of two Pydantic models?","Use `Union[ModelA, ModelB]` (or `ModelA | ModelB`) as the `response_model`.\nFastAPI includes both schemas in the OpenAPI spec.\n\n```python\nfrom typing import Union\n\nclass Cat(BaseModel): name: str; meows: bool\nclass Dog(BaseModel): name: str; barks: bool\n\n@app.get(\"\u002Fpet\u002F{id}\", response_model=Union[Cat, Dog])\nasync def get_pet(id: int) -> Cat | Dog:\n    pet = await db.get_pet(id)\n    return pet   # FastAPI picks the matching model for serialisation\n```\n\nFastAPI serialises using the **first matching model** from left to right in the\nUnion. For discriminated unions, use Pydantic's `discriminator` field for\nunambiguous selection.\n\nRule of thumb: discriminated unions (with a `type: Literal[\"cat\"]` field) are\ncleaner than bare `Union` — they document intent and avoid ambiguous serialisation.\n",{"id":319,"difficulty":63,"q":320,"a":321},"generic-response","How do you create a generic paginated response type in FastAPI?","Use `Generic[T]` from `typing` with a Pydantic `BaseModel`:\n\n```python\nfrom typing import Generic, TypeVar\nfrom pydantic import BaseModel\n\nT = TypeVar(\"T\")\n\nclass Page(BaseModel, Generic[T]):\n    items: list[T]\n    total: int\n    page: int\n    size: int\n\nclass UserOut(BaseModel):\n    id: int\n    name: str\n\n@app.get(\"\u002Fusers\", response_model=Page[UserOut])\nasync def list_users(page: int = 1, size: int = 20):\n    users, total = await db.paginate_users(page, size)\n    return Page(items=users, total=total, page=page, size=size)\n```\n\nPydantic v2 fully supports generic models and FastAPI generates the correct\nOpenAPI schema for each concrete instantiation.\n\nRule of thumb: define one `Page[T]` generic model and reuse it across all list\nendpoints — it keeps pagination schemas consistent and avoids duplication.\n",{"description":31},"FastAPI type hints interview questions — how Python annotations drive parameter parsing, validation, editor support and OpenAPI schema generation.","fastapi\u002Ffundamentals\u002Ftype-hints","Type Hints & FastAPI","Nr51v6fMWEJZeiFZKRQE-kYRliezxU93jinjdgYPU5E",{"id":328,"title":329,"body":330,"description":31,"difficulty":42,"extension":35,"framework":10,"frameworkSlug":8,"meta":334,"navigation":37,"order":335,"path":336,"questions":337,"questionsCount":390,"related":103,"seo":391,"seoDescription":392,"stem":393,"subtopic":394,"topic":19,"topicSlug":20,"updated":107,"__hash__":395},"qa\u002Ffastapi\u002Ffundamentals\u002Fopenapi-docs.md","Openapi Docs",{"type":28,"value":331,"toc":332},[],{"title":31,"searchDepth":32,"depth":32,"links":333},[],{},5,"\u002Ffastapi\u002Ffundamentals\u002Fopenapi-docs",[338,342,346,350,354,358,362,366,370,374,378,382,386],{"id":339,"difficulty":42,"q":340,"a":341},"openapi-auto-generation","How does FastAPI generate an OpenAPI schema automatically?","At application startup FastAPI inspects every registered route — its path,\nHTTP method, type annotations, Pydantic models, `Query`\u002F`Body`\u002F`Header`\ndefinitions and docstrings — and assembles an **OpenAPI 3.x** JSON document.\n\n```python\napp = FastAPI(title=\"My API\", version=\"1.0.0\")\n\n@app.get(\"\u002Fitems\u002F{id}\", summary=\"Fetch an item\")\nasync def get_item(id: int) -> Item:\n    \"\"\"Return a single item by its numeric ID.\"\"\"\n    ...\n# OpenAPI schema available at \u002Fopenapi.json\n```\n\nThe schema drives:\n- `\u002Fdocs` — Swagger UI (interactive browser)\n- `\u002Fredoc` — ReDoc (readable reference)\n- Client code generators (`orval`, `openapi-generator`)\n\nRule of thumb: treat the auto-generated schema as the source of truth for your\nAPI contract; add `summary`, `description`, `responses` and tags to keep it accurate.\n",{"id":343,"difficulty":42,"q":344,"a":345},"swagger-vs-redoc","What is the difference between Swagger UI (`\u002Fdocs`) and ReDoc (`\u002Fredoc`) in FastAPI?","Both render the same OpenAPI schema, but for different audiences:\n\n| | Swagger UI | ReDoc |\n|---|---|---|\n| URL | `\u002Fdocs` | `\u002Fredoc` |\n| Primary use | Interactive testing (try-it-out) | Readable documentation |\n| Layout | Split-pane, request builder | Three-panel, prose-first |\n| Authentication | OAuth2\u002FBearer flow built-in | Read-only |\n\n```python\napp = FastAPI(\n    docs_url=\"\u002Fdocs\",       # default\n    redoc_url=\"\u002Fredoc\",     # default\n)\n```\n\nBoth can be moved or disabled:\n```python\napp = FastAPI(docs_url=None, redoc_url=\"\u002Fapi-docs\")\n```\n\nRule of thumb: expose both; use Swagger UI for development\u002Ftesting, share ReDoc\nlinks for external consumer documentation.\n",{"id":347,"difficulty":42,"q":348,"a":349},"disable-docs-production","How do you disable the API docs in production?","Set `docs_url=None` and `redoc_url=None` (and optionally `openapi_url=None`)\nin the `FastAPI()` constructor. Typically done via an environment flag:\n\n```python\nimport os\nfrom fastapi import FastAPI\n\nDEBUG = os.getenv(\"DEBUG\", \"false\").lower() == \"true\"\n\napp = FastAPI(\n    docs_url=\"\u002Fdocs\" if DEBUG else None,\n    redoc_url=\"\u002Fredoc\" if DEBUG else None,\n    openapi_url=\"\u002Fopenapi.json\" if DEBUG else None,\n)\n```\n\nSetting `openapi_url=None` prevents schema scraping even if someone guesses the\n`\u002Fdocs` URL, since the UI needs the schema to render.\n\nRule of thumb: disable the schema endpoint (`openapi_url=None`) in production —\nleaking your API structure is a security surface even if no credentials are exposed.\n",{"id":351,"difficulty":34,"q":352,"a":353},"custom-openapi","How do you customise the generated OpenAPI schema in FastAPI?","Override `app.openapi()` to intercept and modify the schema dict before it's served:\n\n```python\nfrom fastapi.openapi.utils import get_openapi\n\ndef custom_openapi():\n    if app.openapi_schema:\n        return app.openapi_schema\n    schema = get_openapi(\n        title=\"My API\",\n        version=\"2.0.0\",\n        description=\"Full API description with Markdown\",\n        routes=app.routes,\n    )\n    # add a custom security scheme\n    schema[\"components\"][\"securitySchemes\"] = {\n        \"ApiKey\": {\"type\": \"apiKey\", \"in\": \"header\", \"name\": \"X-API-Key\"}\n    }\n    app.openapi_schema = schema\n    return schema\n\napp.openapi = custom_openapi\n```\n\nRule of thumb: override `app.openapi()` only for cross-cutting schema changes\n(logos, extra security schemes); use per-route `responses={}` for endpoint-level docs.\n",{"id":355,"difficulty":42,"q":356,"a":357},"openapi-tags-metadata","How do you add descriptions to OpenAPI tags?","Pass `openapi_tags` to `FastAPI()`. Each entry maps a tag name to a description\nand optional external documentation link.\n\n```python\ntags_metadata = [\n    {\"name\": \"users\", \"description\": \"Operations with users. The **login** flow.\"},\n    {\"name\": \"items\", \"description\": \"Manage items. So _fancy_\", \"externalDocs\": {\n        \"description\": \"More docs\", \"url\": \"https:\u002F\u002Fexample.com\"\n    }},\n]\n\napp = FastAPI(openapi_tags=tags_metadata)\n```\n\nTags that appear in routes but not in `openapi_tags` still show up in the docs —\nthey just won't have descriptions.\n\nRule of thumb: define `openapi_tags` whenever the API has more than three resource\ngroups — it makes the Swagger UI navigable for external consumers.\n",{"id":359,"difficulty":34,"q":360,"a":361},"bearer-auth-swagger","How do you add Bearer token authentication to Swagger UI in FastAPI?","Declare an `OAuth2PasswordBearer` or `HTTPBearer` security scheme. FastAPI\nadds the \"Authorize\" button to Swagger UI automatically.\n\n```python\nfrom fastapi.security import HTTPBearer\n\nbearer = HTTPBearer()\n\n@app.get(\"\u002Fsecure\", dependencies=[Depends(bearer)])\nasync def secure_endpoint():\n    return {\"ok\": True}\n```\n\nFor a full OAuth2 password flow with the Swagger \"Authorize\" dialog:\n\n```python\nfrom fastapi.security import OAuth2PasswordBearer\n\noauth2_scheme = OAuth2PasswordBearer(tokenUrl=\"\u002Ftoken\")\n\n@app.get(\"\u002Fme\")\nasync def me(token: str = Depends(oauth2_scheme)):\n    ...\n```\n\nRule of thumb: using `OAuth2PasswordBearer` (not just `HTTPBearer`) gives you\nthe full username\u002Fpassword login form in Swagger UI — useful for manual testing.\n",{"id":363,"difficulty":42,"q":364,"a":365},"openapi-version","Which version of the OpenAPI specification does FastAPI generate by default?","FastAPI generates **OpenAPI 3.1.0** schemas by default as of v0.99+\n(previously 3.0.x). OpenAPI 3.1 uses JSON Schema 2020-12 for component schemas,\nwhich means it properly supports `null` types, `$ref` alongside other keywords, etc.\n\n```python\napp = FastAPI()\n# GET \u002Fopenapi.json → {\"openapi\": \"3.1.0\", ...}\n```\n\nIf you need 3.0.x compatibility (e.g., older code generators):\n```python\napp = FastAPI(openapi_version=\"3.0.3\")\n```\n\nRule of thumb: stay on 3.1.0 for new projects; pin to 3.0.x only if your\ntoolchain doesn't support 3.1 yet.\n",{"id":367,"difficulty":34,"q":368,"a":369},"schema-extra-example","How do you add examples to the OpenAPI schema for a Pydantic model?","In Pydantic v2, use `model_config` with `json_schema_extra`:\n\n```python\nfrom pydantic import BaseModel, ConfigDict\n\nclass Item(BaseModel):\n    model_config = ConfigDict(\n        json_schema_extra={\n            \"examples\": [{\"name\": \"Widget\", \"price\": 9.99}]\n        }\n    )\n    name: str\n    price: float\n```\n\nFor field-level examples, use `Field(examples=[...])`:\n\n```python\nfrom pydantic import Field\n\nclass Item(BaseModel):\n    name: str = Field(examples=[\"Widget\", \"Gadget\"])\n    price: float = Field(gt=0, examples=[9.99])\n```\n\nRule of thumb: add at least one realistic example per model — Swagger UI's\n\"Try it out\" pre-fills request bodies from examples, saving testers time.\n",{"id":371,"difficulty":42,"q":372,"a":373},"path-operation-include-in-schema","How do you hide a route from the OpenAPI schema?","Set `include_in_schema=False` in the route decorator. The endpoint still works\n— it just won't appear in `\u002Fopenapi.json` or the docs UI.\n\n```python\n@app.get(\"\u002Finternal\u002Fhealth\", include_in_schema=False)\nasync def health():\n    return {\"status\": \"ok\"}\n```\n\nCommon uses: health-check endpoints, internal debug routes, legacy redirects\nthat you don't want to document publicly.\n\nRule of thumb: use `include_in_schema=False` for infra\u002Fops endpoints that aren't\npart of the public API contract.\n",{"id":375,"difficulty":63,"q":376,"a":377},"api-versioning-strategies","What are the common strategies for API versioning in FastAPI?","**URL prefix versioning** (most common):\n```python\nv1 = APIRouter(prefix=\"\u002Fv1\")\nv2 = APIRouter(prefix=\"\u002Fv2\")\napp.include_router(v1)\napp.include_router(v2)\n```\n\n**Multiple FastAPI apps mounted with `Mount`**:\n```python\nfrom starlette.routing import Mount\nv1_app = FastAPI()\nv2_app = FastAPI()\napp = FastAPI()\napp.mount(\"\u002Fv1\", v1_app)\napp.mount(\"\u002Fv2\", v2_app)\n# Each sub-app has its own \u002Fdocs\n```\n\n**Header versioning** (clean URLs, harder to implement):\n```python\n@app.get(\"\u002Fitems\")\nasync def items(accept_version: str = Header(default=\"v1\")):\n    if accept_version == \"v2\":\n        ...\n```\n\nURL prefix is recommended for REST APIs because it's cacheable, bookmarkable\nand obvious in logs.\n\nRule of thumb: use URL prefix (`\u002Fv1`, `\u002Fv2`) with separate `APIRouter` instances\nper version; mounted sub-apps are the cleanest option when versions diverge significantly.\n",{"id":379,"difficulty":34,"q":380,"a":381},"openapi-response-headers","How do you document custom response headers in the OpenAPI schema?","Add the headers to the `responses` dict in the route decorator under the status\ncode's `headers` key:\n\n```python\n@app.get(\n    \"\u002Fitems\",\n    responses={\n        200: {\n            \"headers\": {\n                \"X-Total-Count\": {\n                    \"description\": \"Total number of items\",\n                    \"schema\": {\"type\": \"integer\"},\n                }\n            }\n        }\n    },\n)\nasync def list_items(response: Response):\n    items = await db.all()\n    response.headers[\"X-Total-Count\"] = str(len(items))\n    return items\n```\n\nRule of thumb: document custom response headers in `responses={}` so consumers\nknow to look for them; set them at runtime by injecting `Response`.\n",{"id":383,"difficulty":63,"q":384,"a":385},"openapi-callbacks","What are OpenAPI callbacks and when would you define them in FastAPI?","**Callbacks** document webhooks that your API will call on the consumer's server\nafter a certain event. They're outbound HTTP calls your API makes, documented\nas if they were inbound routes.\n\n```python\nfrom fastapi import APIRouter\n\ninvoicing_callback = APIRouter()\n\n@invoicing_callback.post(\"{$callback_url}\u002Finvoice\")\ndef invoice_notification(body: InvoiceEvent): ...\n\n@app.post(\"\u002Fsubscriptions\", callbacks=invoicing_callback.routes)\nasync def create_subscription(subscription: Subscription):\n    # after payment succeeds, your server will POST to subscription.callback_url\n    ...\n```\n\nRule of thumb: define callbacks when your API sends webhook notifications —\nit lets consumers generate typed handlers for the events you'll push to them.\n",{"id":387,"difficulty":63,"q":388,"a":389},"swagger-ui-oauth2-config","How do you configure Swagger UI's OAuth2 settings (client ID, scopes) in FastAPI?","Pass `swagger_ui_init_oauth` to `FastAPI()`:\n\n```python\napp = FastAPI(\n    swagger_ui_init_oauth={\n        \"clientId\": \"my-client-id\",\n        \"scopes\": \"openid profile email\",\n        \"usePkceWithAuthorizationCodeGrant\": True,\n    }\n)\n```\n\nThis pre-fills the \"Authorize\" dialog in Swagger UI so developers don't have\nto type the client ID on every test session.\n\nFor full Swagger UI parameter control:\n```python\napp = FastAPI(\n    swagger_ui_parameters={\n        \"deepLinking\": True,\n        \"persistAuthorization\": True,   # keeps auth token across page refreshes\n    }\n)\n```\n\nRule of thumb: set `persistAuthorization: True` in development environments so\ntesters don't lose their JWT token every time they reload Swagger UI.\n",13,{"description":31},"FastAPI OpenAPI and docs interview questions — Swagger UI, ReDoc, schema customisation, security schemes, tags, versioning and disabling docs in production.","fastapi\u002Ffundamentals\u002Fopenapi-docs","OpenAPI & Docs","1i7CKl5qMzgKHDOxS30S_FxxsQvYZtjaUErzhfUAl5A",1782244096187]