[{"data":1,"prerenderedAt":222},["ShallowReactive",2],{"topic-fastapi-dependency-injection":3},{"framework":4,"topic":15,"subtopics":24},{"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":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Ffastapi-dependency-injection.yml","Depends(), yield dependencies, class-based deps and lifespan — FastAPI's DI system is one of its most interview-tested features.",{},"Dependency Injection",4,"dependency-injection","topics\u002Ffastapi-dependency-injection","2VA0cVD1rLFmzQg2_OPMEFHUbgCD2k27Pu5RORd9xGY",[25,97,161],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":90,"related":91,"seo":92,"seoDescription":93,"stem":94,"subtopic":27,"topic":19,"topicSlug":21,"updated":95,"__hash__":96},"qa\u002Ffastapi\u002Fdependency-injection\u002Fdepends-basics.md","Depends Basics",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Ffastapi\u002Fdependency-injection\u002Fdepends-basics",[41,46,50,54,58,62,66,70,74,78,82,86],{"id":42,"difficulty":43,"q":44,"a":45},"depends-basics","easy","What is `Depends()` in FastAPI and why is it useful?","`Depends(fn)` tells FastAPI to call `fn`, pass any of its own parameters through\nthe same injection system, and give the **result** to the handler as the argument.\n\n```python\nfrom fastapi import Depends, FastAPI\n\napp = FastAPI()\n\ndef get_query(q: str | None = None):\n    return q\n\n@app.get(\"\u002Fitems\")\nasync def list_items(query: str | None = Depends(get_query)):\n    return {\"q\": query}\n```\n\nBenefits:\n- **Reusable logic** (auth, DB sessions, pagination) across many handlers.\n- **Testable** — swap dependencies via `app.dependency_overrides`.\n- **Recursive** — a dependency can itself declare `Depends()`.\n\nRule of thumb: extract any logic that repeats across handlers into a dependency\nfunction; if it's in more than two handlers, it probably belongs in a dep.\n",{"id":47,"difficulty":43,"q":48,"a":49},"dependency-parameters","Can a dependency function accept its own parameters (path, query, headers)?","Yes — dependency functions participate in the same parameter extraction system\nas route handlers. They can declare path params, query params, headers, cookies,\nand other `Depends()`.\n\n```python\nfrom fastapi import Depends, Query\n\ndef pagination(\n    page: int = Query(ge=1, default=1),\n    size: int = Query(ge=1, le=100, default=20),\n) -> dict:\n    return {\"skip\": (page - 1) * size, \"limit\": size}\n\n@app.get(\"\u002Fusers\")\nasync def list_users(paging: dict = Depends(pagination)):\n    return await db.get_users(**paging)\n\n@app.get(\"\u002Forders\")\nasync def list_orders(paging: dict = Depends(pagination)):\n    return await db.get_orders(**paging)\n```\n\nRule of thumb: put shared query parameters (pagination, sorting, filtering) into\na dependency class or function — define once, reuse everywhere.\n",{"id":51,"difficulty":35,"q":52,"a":53},"sub-dependencies","Can a dependency depend on another dependency (sub-dependencies)?","Yes — FastAPI resolves the full dependency graph recursively. Sub-dependencies\nare resolved before the dependencies that need them.\n\n```python\ndef get_db():\n    ...\n    yield session\n\ndef get_current_user(db = Depends(get_db)):\n    user = db.query(User).first()\n    return user\n\ndef require_admin(user = Depends(get_current_user)):\n    if not user.is_admin:\n        raise HTTPException(403, \"Admin only\")\n    return user\n\n@app.delete(\"\u002Fusers\u002F{id}\")\nasync def delete_user(id: int, admin = Depends(require_admin)):\n    ...\n```\n\nFastAPI calls `get_db → get_current_user → require_admin` in order, caching each\nresult within the request.\n\nRule of thumb: chain dependencies to encode privilege escalation cleanly —\n`require_admin` composes `get_current_user` which composes `get_db`.\n",{"id":55,"difficulty":35,"q":56,"a":57},"dependency-caching","If two dependencies declare `Depends(get_db)`, how many times is `get_db` called?","**Once per request** (by default). FastAPI caches dependency results within a\nsingle request — the same object is passed to both dependants.\n\n```python\ndef get_db():\n    print(\"get_db called\")\n    yield session\n\ndef dep_a(db = Depends(get_db)): ...\ndef dep_b(db = Depends(get_db)): ...\n\n@app.get(\"\u002F\")\nasync def handler(a = Depends(dep_a), b = Depends(dep_b)):\n    # \"get_db called\" prints once, not twice\n    ...\n```\n\nTo force a fresh call (bypass cache):\n```python\nDepends(get_db, use_cache=False)\n```\n\nRule of thumb: rely on caching to share a single DB session across all\ndependencies in a request — it prevents phantom double-writes or double-reads.\n",{"id":59,"difficulty":35,"q":60,"a":61},"class-based-dep","How do you create a class-based dependency in FastAPI?","Define a class with `__init__` taking FastAPI-injectable parameters, then\nuse the class itself (not an instance) as the argument to `Depends`:\n\n```python\nfrom fastapi import Depends, Query\n\nclass PaginationDep:\n    def __init__(\n        self,\n        page: int = Query(ge=1, default=1),\n        size: int = Query(ge=1, le=100, default=20),\n    ):\n        self.skip = (page - 1) * size\n        self.limit = size\n\n@app.get(\"\u002Fitems\")\nasync def list_items(paging: PaginationDep = Depends()):\n    return await db.get(skip=paging.skip, limit=paging.limit)\n```\n\n`Depends()` (with no argument) is shorthand for `Depends(PaginationDep)` when\nthe type annotation is already the class.\n\nRule of thumb: use class-based dependencies for multi-parameter groups (pagination,\nfiltering) — they're more readable than a function returning a tuple of values.\n",{"id":63,"difficulty":35,"q":64,"a":65},"callable-instance-dep","Can a callable object instance be used as a FastAPI dependency?","Yes — any callable can be a dependency, including instances with `__call__`:\n\n```python\nclass VerifyToken:\n    def __init__(self, required_scope: str):\n        self.required_scope = required_scope\n\n    def __call__(self, token: str = Depends(oauth2_scheme)):\n        payload = decode_jwt(token)\n        if self.required_scope not in payload.get(\"scopes\", []):\n            raise HTTPException(403, \"Insufficient scope\")\n        return payload\n\nverify_read  = VerifyToken(\"read\")\nverify_write = VerifyToken(\"write\")\n\n@app.get(\"\u002Fitems\", dependencies=[Depends(verify_read)])\nasync def list_items(): ...\n\n@app.post(\"\u002Fitems\", dependencies=[Depends(verify_write)])\nasync def create_item(): ...\n```\n\nRule of thumb: use callable instances when a dependency needs to be parameterised\nat definition time (scope, role, feature flag) — it avoids writing multiple\nnear-identical dependency functions.\n",{"id":67,"difficulty":43,"q":68,"a":69},"depends-shorthand","What is the `Depends()` shorthand and when can you use it?","When the type annotation already names the dependency class, you can write\n`Depends()` (no argument) and FastAPI infers the class from the annotation:\n\n```python\nclass CommonQueryParams:\n    def __init__(self, q: str | None = None, skip: int = 0):\n        self.q = q\n        self.skip = skip\n\n@app.get(\"\u002Fitems\")\nasync def list_items(commons: CommonQueryParams = Depends()):\n    #                                                  ^^^ shorthand\n    return commons.__dict__\n```\n\nThis only works in Python 3.10+ (or with `from __future__ import annotations`)\nand requires the annotation to be a class directly, not a string or `Optional`.\n\nRule of thumb: use the shorthand for class-based deps to reduce repetition;\nuse the explicit `Depends(SomeFunction)` for function-based deps (no shorthand there).\n",{"id":71,"difficulty":35,"q":72,"a":73},"path-operation-dependencies","How do you apply a dependency to a route without using its return value?","Pass it to the `dependencies=` list in the route decorator. The dependency runs\nbut its return value is discarded.\n\n```python\nfrom fastapi import Depends\n\nasync def rate_limit():\n    if too_many_requests():\n        raise HTTPException(429, \"Too many requests\")\n\n@app.get(\"\u002Fitems\", dependencies=[Depends(rate_limit)])\nasync def list_items():\n    return []\n```\n\nThe same dependency can be applied at the router level:\n```python\nrouter = APIRouter(dependencies=[Depends(rate_limit)])\n```\n\nRule of thumb: use `dependencies=[...]` for side-effect deps (auth checks,\nrate limiting, audit logging) where the return value is not needed in the handler.\n",{"id":75,"difficulty":35,"q":76,"a":77},"global-dependencies","How do you apply a dependency globally to every route in the app?","Pass `dependencies` to the `FastAPI()` constructor:\n\n```python\nfrom fastapi import FastAPI, Depends\n\nasync def verify_api_key(x_api_key: str = Header()):\n    if x_api_key != settings.api_key:\n        raise HTTPException(401)\n\napp = FastAPI(dependencies=[Depends(verify_api_key)])\n```\n\nEvery single route in the app now runs `verify_api_key` first. To exclude\nspecific routes (e.g., the `\u002Fhealth` check), structure them under a different\nrouter that doesn't inherit the global dependency.\n\nRule of thumb: global dependencies are powerful but inflexible — prefer\nrouter-level dependencies so you can have public and protected route groups.\n",{"id":79,"difficulty":43,"q":80,"a":81},"dependency-return-type","What types can a dependency function return?","Any Python value — FastAPI passes whatever the function returns to the handler\nparameter:\n\n```python\n# returns a simple value\ndef current_user_id(token: str = Depends(oauth2_scheme)) -> int:\n    return decode_jwt(token)[\"sub\"]\n\n# returns a Pydantic model\ndef current_user(token: str = Depends(oauth2_scheme)) -> User:\n    return User.model_validate(decode_jwt(token))\n\n# returns None (side-effect dep)\ndef check_rate_limit():\n    if over_limit():\n        raise HTTPException(429)\n\n@app.get(\"\u002Fme\")\nasync def me(user: User = Depends(current_user)):\n    return user\n```\n\nRule of thumb: annotate the return type of dependency functions — it documents\nwhat the handler receives and helps type checkers catch mismatches.\n",{"id":83,"difficulty":35,"q":84,"a":85},"dependency-exception","What happens if a dependency raises an `HTTPException`?","The exception propagates up through the dependency chain and FastAPI returns the\nerror response to the client — the handler never runs.\n\n```python\nasync def get_current_user(token: str = Depends(oauth2_scheme)):\n    try:\n        payload = jwt.decode(token, SECRET_KEY, algorithms=[\"HS256\"])\n    except JWTError:\n        raise HTTPException(\n            status_code=401,\n            detail=\"Invalid token\",\n            headers={\"WWW-Authenticate\": \"Bearer\"},\n        )\n    return payload\n\n@app.get(\"\u002Fme\")\nasync def me(user: dict = Depends(get_current_user)):\n    return user   # only reached if get_current_user doesn't raise\n```\n\nRule of thumb: raise `HTTPException` in dependencies rather than returning\nsentinel values — the handler should never need to check \"did the dep succeed?\".\n",{"id":87,"difficulty":35,"q":88,"a":89},"testing-dependency-override","How do you override a dependency in tests?","Use `app.dependency_overrides` dict. The key is the original function; the value\nis the replacement callable:\n\n```python\nfrom fastapi.testclient import TestClient\nfrom app.main import app\nfrom app.dependencies import get_current_user\n\ndef mock_user():\n    return {\"sub\": 1, \"role\": \"admin\"}\n\napp.dependency_overrides[get_current_user] = mock_user\n\nclient = TestClient(app)\n\ndef test_admin_endpoint():\n    resp = client.get(\"\u002Fadmin\u002Fstats\")\n    assert resp.status_code == 200\n\n# Clean up after the test\napp.dependency_overrides.clear()\n```\n\nRule of thumb: always clear `dependency_overrides` after each test — a stale\noverride in one test silently affects subsequent tests.\n",12,null,{"description":32},"FastAPI dependency injection interview questions — Depends(), shared sub-dependencies, caching, class-based dependencies and path operation dependencies.","fastapi\u002Fdependency-injection\u002Fdepends-basics","2026-06-20","mPp69gCF-1heEe8Jgp4KWGXtAoVklZZTpM7ZiXN4AS8",{"id":98,"title":99,"body":100,"description":32,"difficulty":104,"extension":36,"framework":10,"frameworkSlug":8,"meta":105,"navigation":38,"order":33,"path":106,"questions":107,"questionsCount":90,"related":91,"seo":156,"seoDescription":157,"stem":158,"subtopic":159,"topic":19,"topicSlug":21,"updated":95,"__hash__":160},"qa\u002Ffastapi\u002Fdependency-injection\u002Fadvanced-deps.md","Advanced Deps",{"type":29,"value":101,"toc":102},[],{"title":32,"searchDepth":33,"depth":33,"links":103},[],"hard",{},"\u002Ffastapi\u002Fdependency-injection\u002Fadvanced-deps",[108,112,116,120,124,128,132,136,140,144,148,152],{"id":109,"difficulty":35,"q":110,"a":111},"yield-dependency","What is a yield dependency in FastAPI and what problem does it solve?","A **yield dependency** is a generator function that uses `yield` to split into\nsetup (before `yield`) and teardown (after `yield`). FastAPI runs the setup,\ninjects the yielded value, runs the handler, then runs the teardown — even if\nthe handler raises an exception.\n\n```python\nfrom sqlalchemy.orm import Session\nfrom app.db import SessionLocal\n\ndef get_db():\n    db = SessionLocal()\n    try:\n        yield db         # handler receives this\n    finally:\n        db.close()       # always runs after the handler\n\n@app.get(\"\u002Fusers\")\nasync def list_users(db: Session = Depends(get_db)):\n    return db.query(User).all()\n```\n\nThis is the standard pattern for DB sessions, file handles, and any resource\nthat must be cleaned up after the request.\n\nRule of thumb: use `yield` deps instead of separate open\u002Fclose functions —\nthe `finally` block guarantees cleanup even when the handler raises.\n",{"id":113,"difficulty":35,"q":114,"a":115},"async-yield-dep","How do you write an async yield dependency?","Use `async def` with `yield` — the same pattern but awaitable:\n\n```python\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom app.db import async_session_factory\n\nasync def get_async_db():\n    async with async_session_factory() as session:\n        yield session\n        await session.commit()   # auto-commit on success\n    # async context manager handles close\n```\n\nOr more explicitly:\n```python\nasync def get_async_db():\n    session = AsyncSession(engine)\n    try:\n        yield session\n        await session.commit()\n    except Exception:\n        await session.rollback()\n        raise\n    finally:\n        await session.close()\n```\n\nRule of thumb: for async databases always use an async yield dep — mixing sync\nSQLAlchemy sessions with `async def` handlers blocks the event loop.\n",{"id":117,"difficulty":104,"q":118,"a":119},"yield-dep-exception","What happens in a yield dependency if the handler raises an exception?","Code **after** `yield` still runs (inside the `finally` block), but code after\n`yield` and outside `finally` does **not** if an exception propagated.\n\n```python\ndef get_db():\n    db = SessionLocal()\n    try:\n        yield db\n        db.commit()    # ← skipped if handler raises\n    except Exception:\n        db.rollback()  # ← runs if handler raises, then re-raises\n        raise\n    finally:\n        db.close()     # ← always runs\n```\n\nFastAPI catches the exception from the handler, runs the dep's `except`\u002F`finally`\nblocks, then re-raises the exception so the error response is still sent to the client.\n\nRule of thumb: always put resource cleanup in `finally`, never rely on code\nafter `yield` running unconditionally — it only runs on success.\n",{"id":121,"difficulty":104,"q":122,"a":123},"multiple-yield-deps","What happens when multiple yield dependencies are active in the same request?","FastAPI runs teardowns in **reverse order** of setup — LIFO (last-in, first-out).\nIf dep A was set up before dep B, B tears down before A.\n\n```python\ndef dep_a():\n    print(\"A setup\")\n    yield \"a\"\n    print(\"A teardown\")   # runs second\n\ndef dep_b():\n    print(\"B setup\")\n    yield \"b\"\n    print(\"B teardown\")   # runs first\n\n@app.get(\"\u002F\")\nasync def handler(a = Depends(dep_a), b = Depends(dep_b)):\n    return {}\n# output: A setup, B setup, B teardown, A teardown\n```\n\nThis mirrors context manager nesting: inner closes before outer.\n\nRule of thumb: if dep B depends on A's resource (e.g., B uses the DB session\nfrom A), declare B's dependency on A explicitly so the order is guaranteed.\n",{"id":125,"difficulty":104,"q":126,"a":127},"background-task-yield-dep","Can a yield dependency's teardown access the response or run background tasks?","Teardown code (after `yield`) runs **after the response is sent** to the client\n— the same lifecycle as `BackgroundTasks`. You cannot modify the response headers\nat this point, but you can perform cleanup I\u002FO.\n\n```python\ndef audit_dep(request: Request):\n    start = time.time()\n    yield\n    elapsed = time.time() - start\n    # log_access(request.url, elapsed)   ← safe here, response already sent\n```\n\nYou **cannot** raise `HTTPException` in teardown (after yield) and expect it to\nchange the response — the response is already sent.\n\nRule of thumb: teardown code is for cleanup only (close sessions, release locks,\nwrite audit logs); never try to alter the HTTP response there.\n",{"id":129,"difficulty":35,"q":130,"a":131},"middleware-vs-dependency","When should you use middleware vs a dependency for cross-cutting concerns?","| Concern | Middleware | Dependency |\n|---------|-----------|-----------|\n| Applies to ALL routes | ✅ easy | Needs global `dependencies=` |\n| Access to response body | ✅ | ❌ |\n| Access to handler's return value | ❌ | ✅ (yield after) |\n| Testable via `dependency_overrides` | ❌ | ✅ |\n| Needs route parameters | ❌ (no routing yet) | ✅ |\n| Short-circuit before routing | ✅ | ❌ |\n\n```python\n# Middleware — good for request timing, response modification\nclass TimingMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request, call_next):\n        start = time.time()\n        response = await call_next(request)\n        response.headers[\"X-Process-Time\"] = str(time.time() - start)\n        return response\n\n# Dependency — good for auth (needs decoded token in handler)\nasync def get_current_user(token = Depends(oauth2_scheme)): ...\n```\n\nRule of thumb: use middleware for HTTP-level concerns (timing, CORS, compression);\nuse dependencies for application-level concerns (auth, DB sessions, feature flags).\n",{"id":133,"difficulty":35,"q":134,"a":135},"parametrised-dep","How do you create a parameterised dependency (different instances with different config)?","Return a dependency function from a factory function:\n\n```python\nfrom fastapi import Depends, HTTPException\n\ndef require_role(role: str):\n    def _check(user: User = Depends(get_current_user)):\n        if user.role != role:\n            raise HTTPException(403, f\"Role '{role}' required\")\n        return user\n    return _check\n\n@app.get(\"\u002Fadmin\", dependencies=[Depends(require_role(\"admin\"))])\nasync def admin_panel(): ...\n\n@app.get(\"\u002Freports\", dependencies=[Depends(require_role(\"analyst\"))])\nasync def reports(): ...\n```\n\nOr use a class with `__call__` (see \"callable instance dep\" pattern).\n\nRule of thumb: parametrised deps via factory functions are cleaner than a dozen\n`require_admin`, `require_analyst`, etc. — the role is a parameter, not a copy.\n",{"id":137,"difficulty":35,"q":138,"a":139},"context-manager-dep","How do you turn an existing async context manager into a FastAPI dependency?","Use `contextlib.asynccontextmanager` or just write the yield dep directly. If\nthe resource is already an async context manager, delegate to it:\n\n```python\nimport httpx\nfrom fastapi import Depends\n\nasync def get_http_client():\n    async with httpx.AsyncClient() as client:\n        yield client   # handler gets the open client\n\n@app.get(\"\u002Fproxy\")\nasync def proxy(client: httpx.AsyncClient = Depends(get_http_client)):\n    resp = await client.get(\"https:\u002F\u002Fapi.example.com\u002Fdata\")\n    return resp.json()\n```\n\nFastAPI closes the `AsyncClient` after the handler runs.\n\nRule of thumb: wrap third-party async context managers (httpx, aiofiles) in\nyield deps — you get proper lifecycle management without handler boilerplate.\n",{"id":141,"difficulty":104,"q":142,"a":143},"dep-caching-across-requests","Is dependency caching per-request or across requests?","**Per-request only**. FastAPI creates a new dependency cache for each incoming\nrequest; there is no cross-request singleton via `Depends()`.\n\nFor cross-request singletons (connection pools, ML models, HTTP clients):\n- Store them on `app.state` in the `lifespan` function.\n- Or use a module-level variable \u002F `@lru_cache`.\n\n```python\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    app.state.http_client = httpx.AsyncClient()\n    yield\n    await app.state.http_client.aclose()\n\n@app.get(\"\u002Fproxy\")\nasync def proxy(request: Request):\n    client = request.app.state.http_client   # cross-request singleton\n    return await client.get(\"https:\u002F\u002Fapi.example.com\u002F\")\n```\n\nRule of thumb: use `lifespan` + `app.state` for long-lived resources (DB pool,\nML model); use `Depends()` for per-request resources (sessions, auth context).\n",{"id":145,"difficulty":104,"q":146,"a":147},"dep-vs-middleware-order","In what order do global dependencies, middleware, and router dependencies execute?","```\nRequest →\n  Middleware (outermost first) →\n    FastAPI routing →\n      Global app dependencies →\n        Router-level dependencies →\n          Route-level dependencies →\n            Handler body\n          ← Route-level dep teardown\n        ← Router-level dep teardown\n      ← Global dep teardown\n    ← FastAPI exception handling\n  ← Middleware (innermost first on way out)\nResponse\n```\n\nMiddleware wraps everything, including dependency setup\u002Fteardown. Global deps\nset up before router deps, which set up before route deps.\n\nRule of thumb: auth middleware that checks a header before FastAPI even routes\nthe request is faster than a dep (no routing overhead) but less testable; choose\nbased on whether you need route-aware logic.\n",{"id":149,"difficulty":35,"q":150,"a":151},"dep-security-scheme","How do FastAPI security utilities (`OAuth2PasswordBearer`, `HTTPBearer`) relate to `Depends()`?","Security utilities are **callable classes** that implement `SecurityBase`. When\nyou use them with `Depends()`, FastAPI:\n1. Extracts the token (from header\u002Fcookie\u002Fquery).\n2. Adds the scheme to the OpenAPI security definitions automatically.\n3. Makes Swagger UI show the \"Authorize\" button.\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    # token is the Bearer value from \"Authorization: Bearer \u003Ctoken>\"\n    ...\n```\n\nWithout security utilities, you'd read the header manually and lose the OpenAPI\nintegration.\n\nRule of thumb: always use FastAPI's security utilities (`OAuth2PasswordBearer`,\n`HTTPBearer`, `APIKeyHeader`) instead of plain `Header()` for auth — the OpenAPI\nintegration is a meaningful developer-experience benefit.\n",{"id":153,"difficulty":43,"q":154,"a":155},"dep-inject-request","How do you access the raw `Request` object inside a FastAPI dependency?","Declare `Request` as a parameter of the dependency function — FastAPI injects it\nautomatically without `Depends()`:\n\n```python\nfrom fastapi import Request, Depends\n\ndef get_client_ip(request: Request) -> str:\n    return request.client.host\n\n@app.get(\"\u002Fitems\")\nasync def list_items(ip: str = Depends(get_client_ip)):\n    return {\"client_ip\": ip}\n```\n\nThe `Request` object gives access to headers, cookies, query string, client\naddress and the raw body stream.\n\nRule of thumb: inject `Request` into dependencies that need HTTP metadata\n(rate limiting by IP, request ID logging) — keep handlers clean of low-level\nHTTP details.\n",{"description":32},"FastAPI advanced dependency injection interview questions — yield dependencies, resource cleanup, middleware vs dependency, async generators and exception handling.","fastapi\u002Fdependency-injection\u002Fadvanced-deps","Advanced Dependencies","Kd6_1pFLpUON6E3jNf0HEsarOU83z-p0hxz5UcMwXYo",{"id":162,"title":163,"body":164,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":168,"navigation":38,"order":169,"path":170,"questions":171,"questionsCount":216,"related":91,"seo":217,"seoDescription":218,"stem":219,"subtopic":220,"topic":19,"topicSlug":21,"updated":95,"__hash__":221},"qa\u002Ffastapi\u002Fdependency-injection\u002Flifespan.md","Lifespan",{"type":29,"value":165,"toc":166},[],{"title":32,"searchDepth":33,"depth":33,"links":167},[],{},3,"\u002Ffastapi\u002Fdependency-injection\u002Flifespan",[172,176,180,184,188,192,196,200,204,208,212],{"id":173,"difficulty":43,"q":174,"a":175},"lifespan-basics","What is the `lifespan` parameter in FastAPI and what does it replace?","`lifespan` is an `async` context manager function that runs **startup** code\nbefore `yield` and **shutdown** code after it. It was introduced to replace\nthe older `@app.on_event(\"startup\")` \u002F `@app.on_event(\"shutdown\")` decorators,\nwhich are deprecated.\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    print(\"DB connected\")\n    yield\n    # shutdown\n    await db_pool.disconnect()\n    print(\"DB disconnected\")\n\napp = FastAPI(lifespan=lifespan)\n```\n\nRule of thumb: always use `lifespan=` for new apps — it's the modern approach\nand groups startup\u002Fshutdown logic in one readable function.\n",{"id":177,"difficulty":43,"q":178,"a":179},"on-event-deprecated","Why are `@app.on_event(\"startup\")` decorators deprecated and what should you use instead?","The decorator approach had two problems:\n1. Startup and shutdown logic is split across two separate functions — harder\n   to read the resource lifecycle.\n2. You can't use `async with` or `yield`-based context managers in event handlers.\n\nThe `lifespan` context manager fixes both:\n\n```python\n# Old (deprecated)\n@app.on_event(\"startup\")\nasync def startup():\n    await cache.connect()\n\n@app.on_event(\"shutdown\")\nasync def shutdown():\n    await cache.disconnect()\n\n# New (preferred)\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    await cache.connect()\n    yield\n    await cache.disconnect()\n\napp = FastAPI(lifespan=lifespan)\n```\n\nRule of thumb: migrate `@app.on_event` to `lifespan` in any new or refactored\ncodebase — it's just moving the two functions into a context manager.\n",{"id":181,"difficulty":35,"q":182,"a":183},"app-state","How do you share objects (DB pool, HTTP client) across all requests in FastAPI?","Store them on `app.state` inside the `lifespan` function. `app.state` is a\n`Starlette` `State` object — a simple namespace you can attach anything to.\n\n```python\nimport httpx\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI, Request\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    app.state.http_client = httpx.AsyncClient()\n    yield\n    await app.state.http_client.aclose()\n\napp = FastAPI(lifespan=lifespan)\n\n@app.get(\"\u002Fproxy\")\nasync def proxy(request: Request):\n    client = request.app.state.http_client\n    resp = await client.get(\"https:\u002F\u002Fapi.example.com\u002Fdata\")\n    return resp.json()\n```\n\nRule of thumb: use `app.state` for truly shared singletons (connection pools,\nloaded ML models, config); use `Depends()` for per-request resources.\n",{"id":185,"difficulty":35,"q":186,"a":187},"lifespan-with-db","Show the standard pattern for initialising an async SQLAlchemy engine in lifespan.","```python\nfrom contextlib import asynccontextmanager\nfrom sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker\nfrom fastapi import FastAPI\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    engine = create_async_engine(settings.database_url, pool_size=10)\n    app.state.db_factory = async_sessionmaker(engine, expire_on_commit=False)\n    yield\n    await engine.dispose()\n\napp = FastAPI(lifespan=lifespan)\n\n# Dependency that uses the shared engine\nasync def get_db(request: Request):\n    async with request.app.state.db_factory() as session:\n        yield session\n```\n\nThe engine (and its connection pool) is created once at startup and disposed\ngracefully at shutdown.\n\nRule of thumb: create the engine in `lifespan`, not at module level — it lets\nyou inject different engines in tests via `lifespan` override.\n",{"id":189,"difficulty":104,"q":190,"a":191},"composing-lifespans","How do you compose multiple startup\u002Fshutdown concerns without one giant lifespan function?","Nest `asynccontextmanager` functions inside the main lifespan:\n\n```python\nfrom contextlib import asynccontextmanager\n\n@asynccontextmanager\nasync def db_lifespan(app):\n    engine = create_async_engine(settings.db_url)\n    app.state.engine = engine\n    yield\n    await engine.dispose()\n\n@asynccontextmanager\nasync def cache_lifespan(app):\n    app.state.redis = Redis.from_url(settings.redis_url)\n    yield\n    await app.state.redis.aclose()\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    async with db_lifespan(app):\n        async with cache_lifespan(app):\n            yield\n\napp = FastAPI(lifespan=lifespan)\n```\n\nEach sub-lifespan owns its resource; teardown runs in reverse nesting order.\n\nRule of thumb: one context manager per resource — compose them in the main\n`lifespan` rather than putting everything into one giant function.\n",{"id":193,"difficulty":35,"q":194,"a":195},"lifespan-state-access","How do you access `app.state` from inside a dependency or middleware?","Three ways, depending on context:\n\n**In a dependency** — inject `Request`:\n```python\ndef get_client(request: Request) -> httpx.AsyncClient:\n    return request.app.state.http_client\n```\n\n**In middleware** — `request.app.state` is available the same way:\n```python\nasync def dispatch(self, request: Request, call_next):\n    client = request.app.state.http_client\n    ...\n```\n\n**At module level** — capture the `app` reference directly (only if in the\nsame module):\n```python\nclient = app.state.http_client   # only works after lifespan startup\n```\n\nRule of thumb: always access `app.state` through `request.app` in deps and\nmiddleware — it avoids circular imports and works correctly in tests with\n`TestClient`.\n",{"id":197,"difficulty":104,"q":198,"a":199},"lifespan-testing","How do you test lifespan events in FastAPI?","`TestClient` runs the full lifespan when used as a context manager:\n\n```python\nfrom fastapi.testclient import TestClient\nfrom app.main import app\n\ndef test_lifespan_sets_state():\n    with TestClient(app) as client:\n        # startup has run; app.state is populated\n        resp = client.get(\"\u002Fhealth\")\n        assert resp.status_code == 200\n    # shutdown has run; resources are closed\n```\n\nFor async tests with `httpx.AsyncClient`:\n```python\nfrom httpx import AsyncClient, ASGITransport\nimport pytest\n\n@pytest.mark.anyio\nasync def test_endpoint():\n    async with AsyncClient(\n        transport=ASGITransport(app=app), base_url=\"http:\u002F\u002Ftest\"\n    ) as client:\n        resp = await client.get(\"\u002Fitems\")\n        assert resp.status_code == 200\n```\n\nRule of thumb: always use `TestClient` as a context manager (`with TestClient(app)`)\nso startup and shutdown run — a non-context-manager `TestClient` skips them.\n",{"id":201,"difficulty":104,"q":202,"a":203},"lifespan-exception-handling","What happens if an exception is raised during startup in the `lifespan` function?","If an exception propagates out of the code before `yield`, FastAPI never starts\naccepting requests — the application exits. The teardown code (after `yield`)\ndoes **not** run because `yield` was never reached.\n\n```python\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    try:\n        await db.connect()        # if this raises, app won't start\n    except ConnectionError as e:\n        log.critical(\"DB unavailable: %s\", e)\n        raise                     # startup fails, process exits\n    yield\n    await db.disconnect()         # never reached if startup failed\n```\n\nFor resources that you want to clean up even if a later startup step fails,\nnest the `try\u002Ffinally` around each step individually.\n\nRule of thumb: let startup exceptions propagate — a crashed startup is\nbetter than a running app with a broken DB connection.\n",{"id":205,"difficulty":35,"q":206,"a":207},"router-vs-app-lifespan","Can `APIRouter` have its own lifespan?","No — `APIRouter` does not support `lifespan`. Only the root `FastAPI` instance\naccepts a lifespan function. To organise per-module startup logic, use helper\ncontext managers that the root `lifespan` composes:\n\n```python\n# routers\u002Fusers.py\n@asynccontextmanager\nasync def users_lifespan(app):\n    app.state.user_cache = {}\n    yield\n    app.state.user_cache.clear()\n\n# main.py\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    async with users_lifespan(app):\n        async with orders_lifespan(app):\n            yield\n\napp = FastAPI(lifespan=lifespan)\n```\n\nRule of thumb: keep the root `lifespan` as a compositor; define per-module\nstartup as `asynccontextmanager` functions and import them into main.\n",{"id":209,"difficulty":35,"q":210,"a":211},"background-startup","How do you run a background task continuously from application startup?","Use `anyio.create_task_group()` or `asyncio.create_task()` inside the lifespan:\n\n```python\nimport asyncio\nfrom contextlib import asynccontextmanager\n\nasync def poll_metrics():\n    while True:\n        await push_metrics_to_monitoring()\n        await asyncio.sleep(60)\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    task = asyncio.create_task(poll_metrics())\n    yield\n    task.cancel()\n    try:\n        await task\n    except asyncio.CancelledError:\n        pass\n```\n\nAlways cancel and await the task on shutdown — leaving it running causes\n\"Task was destroyed but it is pending!\" warnings in tests.\n\nRule of thumb: cancel background tasks explicitly in the shutdown phase;\nstore the `Task` handle on `app.state` if other parts of the app need to check it.\n",{"id":213,"difficulty":43,"q":214,"a":215},"request-state","What is `request.state` and how does it differ from `app.state`?","`request.state` is a per-request `State` object — separate for every HTTP\nrequest, discarded after the response is sent. `app.state` lives for the\nentire application lifetime.\n\n```python\n# Middleware sets per-request data\nclass RequestIDMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request: Request, call_next):\n        request.state.request_id = str(uuid4())\n        return await call_next(request)\n\n# Handler or dependency reads it\n@app.get(\"\u002Fitems\")\nasync def list_items(request: Request):\n    rid = request.state.request_id\n    return {\"request_id\": rid}\n```\n\nRule of thumb: use `request.state` for per-request metadata (trace IDs, auth\ncontext set by middleware); use `app.state` for shared singletons.\n",11,{"description":32},"FastAPI lifespan interview questions — asynccontextmanager, startup\u002Fshutdown, app.state, resource pooling and testing with lifespan.","fastapi\u002Fdependency-injection\u002Flifespan","Lifespan & App State","TvEk1ojPF6D2IHDvxb_Q10SSpFcZyCAfjEuwKc2-RqM",1782244096220]