[{"data":1,"prerenderedAt":107},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle":3},{"page":4,"siblings":89,"blog":81},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":80,"related":81,"seo":82,"seoDescription":83,"stem":84,"subtopic":6,"topic":85,"topicSlug":86,"updated":87,"__hash__":88},"qa\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle.md","Request Lifecycle",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,"\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle",[22,26,30,35,39,43,48,52,56,60,64,68,72,76],{"id":23,"difficulty":14,"q":24,"a":25},"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":27,"difficulty":14,"q":28,"a":29},"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":31,"difficulty":32,"q":33,"a":34},"http-exception","easy","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":36,"difficulty":14,"q":37,"a":38},"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":40,"difficulty":32,"q":41,"a":42},"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":44,"difficulty":45,"q":46,"a":47},"dependency-resolution-order","hard","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":49,"difficulty":14,"q":50,"a":51},"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":53,"difficulty":14,"q":54,"a":55},"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":57,"difficulty":14,"q":58,"a":59},"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":61,"difficulty":32,"q":62,"a":63},"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":65,"difficulty":32,"q":66,"a":67},"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":69,"difficulty":14,"q":70,"a":71},"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":73,"difficulty":32,"q":74,"a":75},"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":77,"difficulty":14,"q":78,"a":79},"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,null,{"description":11},"FastAPI request lifecycle interview questions — middleware chain, routing, dependency resolution, validation, handler execution and response serialization.","fastapi\u002Ffundamentals\u002Frequest-lifecycle","Fundamentals","fundamentals","2026-06-20","FiKs69biTzPCIi3Wz4Yb1o_O1sXxKJrz07q25xL6GBg",[90,94,95,99,103],{"subtopic":91,"path":92,"order":93},"Async Basics","\u002Ffastapi\u002Ffundamentals\u002Fasync-basics",1,{"subtopic":6,"path":20,"order":12},{"subtopic":96,"path":97,"order":98},"Path Operations","\u002Ffastapi\u002Ffundamentals\u002Fpath-operations",3,{"subtopic":100,"path":101,"order":102},"Type Hints & FastAPI","\u002Ffastapi\u002Ffundamentals\u002Ftype-hints",4,{"subtopic":104,"path":105,"order":106},"OpenAPI & Docs","\u002Ffastapi\u002Ffundamentals\u002Fopenapi-docs",5,1782244112542]