[{"data":1,"prerenderedAt":4014},["ShallowReactive",2],{"page-\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002F":3,"nav":3443,"surround-\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002F":4013},{"id":4,"title":5,"body":6,"dateModified":3411,"datePublished":3411,"description":3412,"extension":3413,"faq":3414,"howto":3426,"meta":3427,"navigation":769,"path":3438,"seo":3439,"stem":3440,"type":3441,"__hash__":3442},"content\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex.md","The FastAPI Request\u002FResponse Lifecycle",{"type":7,"value":8,"toc":3392},"minimark",[9,13,17,41,145,150,162,166,223,235,246,250,257,377,380,399,411,422,426,440,471,482,486,497,513,528,544,548,567,573,577,584,620,646,650,664,668,671,703,717,729,733,740,1831,1838,1845,1856,1886,1890,1902,2940,2947,2953,2986,2990,2993,3012,3018,3031,3035,3040,3047,3053,3065,3069,3147,3151,3268,3276,3280,3292,3307,3316,3322,3336,3340,3388],[10,11,5],"h1",{"id":12},"the-fastapi-requestresponse-lifecycle",[14,15,16],"p",{},"A FastAPI request is not a function call. It is a descent through half a dozen nested layers, each of which can short-circuit, rewrite or observe what passes through it, followed by an ascent back out through the same layers in reverse. Almost every confusing FastAPI bug — a header that never appears on error responses, a dependency that runs twice, a session that closes too early, a field that vanishes from a response — is a misunderstanding of where in that descent something sits.",[14,18,19,20,25,26,30,31,35,36,40],{},"This guide is the map. It belongs to ",[21,22,24],"a",{"href":23},"\u002Fcore-architecture-routing-patterns\u002F","Core Architecture and Routing Patterns",", and it is the layer beneath ",[21,27,29],{"href":28},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","Middleware Implementation",", ",[21,32,34],{"href":33},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","Dependency Injection Strategies"," and ",[21,37,39],{"href":38},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002F","Error Handling and Global Exceptions",": each of those explains one stage well, and this page explains how the stages compose.",[42,43,49,50,49,54,49,58,49,66,49,73,49,81,49,86,49,92,49,97,49,105,49,111,49,118,49,123,49,130,49,136,49,141],"svg",{"viewBox":44,"role":45,"ariaLabel":46,"xmlns":47,"style":48},"0 0 720 420","img","Nested layers of the FastAPI request lifecycle, from ServerErrorMiddleware on the outside to the path operation at the centre","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[51,52,53],"title",{},"The nested layers a FastAPI request passes through",[55,56,57],"desc",{},"Six concentric rectangles. From outside in: ServerErrorMiddleware, your middleware added last, your middleware added first, ExceptionMiddleware, the router and dependency resolution, and the path operation at the centre. A request travels inward and the response travels back outward through the same layers.",[59,60],"rect",{"x":61,"y":61,"width":62,"height":63,"rx":64,"style":65},"10","700","330","12","fill:none;stroke:currentColor;stroke-width:1.5",[67,68,72],"text",{"x":69,"y":70,"style":71},"26","32","text-anchor:start;fill:currentColor;font:600 12px sans-serif","ServerErrorMiddleware — last resort 500",[59,74],{"x":75,"y":76,"width":77,"height":78,"rx":79,"style":80},"55","42","610","272","11","fill:none;stroke:currentColor;stroke-width:1.4",[67,82,85],{"x":83,"y":84,"style":71},"71","63","Your middleware — added LAST, outermost",[59,87],{"x":88,"y":89,"width":90,"height":91,"rx":61,"style":80},"100","72","520","214",[67,93,96],{"x":94,"y":95,"style":71},"116","93","Your middleware — added FIRST, innermost",[59,98],{"x":99,"y":100,"width":101,"height":102,"rx":103,"style":104},"145","102","430","156","9","fill:none;stroke:#00796B;stroke-width:1.6",[67,106,110],{"x":107,"y":108,"style":109},"161","123","text-anchor:start;fill:#00796B;font:600 12px sans-serif","ExceptionMiddleware — your handlers",[59,112],{"x":113,"y":114,"width":115,"height":116,"rx":117,"style":80},"190","132","340","98","8",[67,119,122],{"x":120,"y":121,"style":71},"206","153","Router → dependency resolution",[59,124],{"x":125,"y":126,"width":127,"height":128,"rx":117,"style":129},"212","164","296","46","fill:#00796B;stroke:#00796B;stroke-width:1.4",[67,131,135],{"x":132,"y":133,"style":134},"360","192","text-anchor:middle;fill:#ffffff;font:700 13px sans-serif","Path operation + response_model",[67,137,140],{"x":132,"y":138,"style":139},"368","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","Descent: outermost first. Ascent: innermost first.",[67,142,144],{"x":132,"y":143,"style":139},"392","ServerErrorMiddleware is always outside everything you add.",[146,147,149],"h2",{"id":148},"prerequisites","Prerequisites",[14,151,152,153,157,158,161],{},"You should be comfortable with the ASGI callable signature — ",[154,155,156],"code",{},"async def app(scope, receive, send)"," — and with the fact that FastAPI is a subclass of Starlette's ",[154,159,160],{},"Starlette"," application. Everything below is Starlette machinery that FastAPI decorates rather than replaces. The versions in play here are FastAPI 0.139.2, Starlette 1.3.1 and Pydantic 2.13.4 on Python 3.12; the stack layout has been stable for a long time, but the fine interleaving of teardown discussed later is genuinely version-sensitive.",[146,163,165],{"id":164},"stage-0-the-asgi-scope","Stage 0: the ASGI scope",[14,167,168,169,172,173,30,176,30,179,30,182,30,185,30,188,30,191,194,195,30,198,201,202,205,206,210,211,214,215,218,219,222],{},"Before any of your code runs, the server (Uvicorn, Hypercorn, Granian) has already parsed the request line and headers and built a ",[154,170,171],{},"scope"," dictionary: ",[154,174,175],{},"type",[154,177,178],{},"http_version",[154,180,181],{},"method",[154,183,184],{},"path",[154,186,187],{},"raw_path",[154,189,190],{},"query_string",[154,192,193],{},"headers"," as a list of byte pairs, ",[154,196,197],{},"client",[154,199,200],{},"server",", and an ",[154,203,204],{},"app"," reference. The body is deliberately ",[207,208,209],"em",{},"not"," in the scope. It is available only by awaiting ",[154,212,213],{},"receive()",", which yields ",[154,216,217],{},"http.request"," messages until one arrives with ",[154,220,221],{},"more_body"," false.",[14,224,225,226,230,231,234],{},"This matters more than it looks. The body is a stream that can be consumed exactly once unless someone caches it. Any layer that awaits it — a middleware that wants to log payloads, for example — has taken it away from everyone downstream unless it puts it back. That is the mechanical reason the advice in ",[21,227,229],{"href":228},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F","Middleware vs Dependencies: When to Use Which"," pushes body inspection into dependencies, where FastAPI has already cached the parsed body on the ",[154,232,233],{},"Request"," object.",[14,236,237,238,241,242,245],{},"The scope at this point also has no ",[154,239,240],{},"route"," and no ",[154,243,244],{},"path_params",". Routing has not happened. Middleware that wants to label a metric with the route template rather than the raw path cannot read it on the way in — a constraint that surprises people building Prometheus exporters.",[146,247,249],{"id":248},"stage-1-the-middleware-stack-and-why-order-inverts","Stage 1: the middleware stack, and why order inverts",[14,251,252,253,256],{},"Starlette does not run middleware from a list at request time. It builds a single nested ASGI callable once, lazily, on the first request (or when you access ",[154,254,255],{},"app.middleware_stack","). The construction is, in essence:",[258,259,264],"pre",{"className":260,"code":261,"language":262,"meta":263,"style":263},"language-python shiki shiki-themes github-light-high-contrast","# Conceptually what Starlette's build_middleware_stack() does.\nstack = [ServerErrorMiddleware] + self.user_middleware + [ExceptionMiddleware]\napp = self.router\nfor cls, args, kwargs in reversed(stack):\n    app = cls(app, *args, **kwargs)\nself.middleware_stack = app\n","python","",[154,265,266,275,304,317,338,363],{"__ignoreMap":263},[267,268,271],"span",{"class":269,"line":270},"line",1,[267,272,274],{"class":273},"sFeEa","# Conceptually what Starlette's build_middleware_stack() does.\n",[267,276,278,282,286,289,292,296,299,301],{"class":269,"line":277},2,[267,279,281],{"class":280},"sigWx","stack ",[267,283,285],{"class":284},"sTJeM","=",[267,287,288],{"class":280}," [ServerErrorMiddleware] ",[267,290,291],{"class":284},"+",[267,293,295],{"class":294},"sacAq"," self",[267,297,298],{"class":280},".user_middleware ",[267,300,291],{"class":284},[267,302,303],{"class":280}," [ExceptionMiddleware]\n",[267,305,307,310,312,314],{"class":269,"line":306},3,[267,308,309],{"class":280},"app ",[267,311,285],{"class":284},[267,313,295],{"class":294},[267,315,316],{"class":280},".router\n",[267,318,320,323,326,329,332,335],{"class":269,"line":319},4,[267,321,322],{"class":284},"for",[267,324,325],{"class":294}," cls",[267,327,328],{"class":280},", args, kwargs ",[267,330,331],{"class":284},"in",[267,333,334],{"class":294}," reversed",[267,336,337],{"class":280},"(stack):\n",[267,339,341,344,346,348,351,354,357,360],{"class":269,"line":340},5,[267,342,343],{"class":280},"    app ",[267,345,285],{"class":284},[267,347,325],{"class":294},[267,349,350],{"class":280},"(app, ",[267,352,353],{"class":284},"*",[267,355,356],{"class":280},"args, ",[267,358,359],{"class":284},"**",[267,361,362],{"class":280},"kwargs)\n",[267,364,366,369,372,374],{"class":269,"line":365},6,[267,367,368],{"class":294},"self",[267,370,371],{"class":280},".middleware_stack ",[267,373,285],{"class":284},[267,375,376],{"class":280}," app\n",[14,378,379],{},"Two facts follow, and together they explain the inversion everyone trips over.",[14,381,382,383,386,387,390,391,398],{},"First, ",[154,384,385],{},"add_middleware"," does not append — it inserts at position zero of ",[154,388,389],{},"user_middleware",". Second, the stack is assembled by wrapping from the innermost outward. The net effect is that ",[392,393,394,395,397],"strong",{},"each ",[154,396,385],{}," call wraps everything registered before it",". The last middleware you register is the outermost layer of the onion, so it is the first to see the request and the last to see the response.",[14,400,401,402,405,406,410],{},"This is not arbitrary. It makes the API compositional: whatever you add is guaranteed to observe everything already present. A timing middleware added at the end of ",[154,403,404],{},"create_app()"," measures the cost of every other middleware, which is exactly what you want. If the ordering were the other way round, adding an observer would require you to remember to register it first, and any later addition would silently escape observation. The ordering rules and their consequences are worked through in detail in ",[21,407,409],{"href":408},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002F","Middleware Execution Order",".",[14,412,413,414,417,418,421],{},"Note the two bookends Starlette adds itself. ",[154,415,416],{},"ServerErrorMiddleware"," is always the outermost frame, outside anything you register. ",[154,419,420],{},"ExceptionMiddleware"," is always the innermost, sitting directly around the router. Neither position is configurable, and both positions are load-bearing — see the exception handling section below.",[146,423,425],{"id":424},"stage-2-routing","Stage 2: routing",[14,427,428,429,431,432,435,436,439],{},"Once the request reaches ",[154,430,420],{}," and is passed to the ",[154,433,434],{},"Router",", Starlette walks its route table in registration order, calling ",[154,437,438],{},"matches(scope)"," on each. A route returns a full match, a partial match (path matched but method did not), or no match. The first full match wins; if only partial matches were found, the router raises a 405; otherwise a 404.",[14,441,442,443,446,447,35,449,451,452,455,456,459,460,35,462,465,466,470],{},"Crucially, matching ",[392,444,445],{},"mutates the scope",": on a match, the router writes ",[154,448,244],{},[154,450,240],{}," into it. From this point inward, and only from this point, ",[154,453,454],{},"request.scope[\"route\"]"," exists. This is also where mounted sub-applications hand off — a ",[154,457,458],{},"Mount"," re-writes ",[154,461,184],{},[154,463,464],{},"root_path"," before delegating, which is why the trade-offs in ",[21,467,469],{"href":468},"\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002F","APIRouter Prefix vs Sub-Application Mounting"," come down to scope rewriting rather than syntax.",[14,472,473,474,477,478,481],{},"FastAPI's ",[154,475,476],{},"APIRoute"," then takes over with its generated ",[154,479,480],{},"get_route_handler()"," closure. Everything from here to the serialized response body happens inside that one closure.",[146,483,485],{"id":484},"stage-3-dependency-resolution","Stage 3: dependency resolution",[14,487,488,489,492,493,496],{},"FastAPI walks the dependency graph it computed at import time — not at request time. When you declared the route, FastAPI introspected the signature, resolved every ",[154,490,491],{},"Depends",", recursed into their signatures, and froze the result into a ",[154,494,495],{},"Dependant"," tree. At request time it simply solves that tree, depth first.",[14,498,499,500,503,504,507,508,512],{},"The traversal order is: dependencies declared on the application, then on the router, then on the path operation decorator's ",[154,501,502],{},"dependencies=[]"," list, then the parameters in the handler's own signature — and within each of those, sub-dependencies before their parents. Results are memoised per request keyed by ",[154,505,506],{},"(callable, security_scopes)",", which is why the same provider used in three places runs once; ",[21,509,511],{"href":510},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002F","Dependency Caching and use_cache"," covers when that memoisation helps and when you need to defeat it.",[14,514,515,516,519,520,523,524,410],{},"Request parsing is ",[207,517,518],{},"part of"," this stage, not a separate one. Path, query, header, cookie and body parameters are validated together, and if any fail, FastAPI raises ",[154,521,522],{},"RequestValidationError"," before your handler body executes. That single collected error is why a bad request yields every problem at once rather than one at a time — the mechanics are unpacked in ",[21,525,527],{"href":526},"\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002F","Query, Path and Body Parameter Validation",[14,529,530,531,534,535,538,539,543],{},"Dependencies written with ",[154,532,533],{},"yield"," are entered here but not exited. FastAPI pushes each one onto an ",[154,536,537],{},"AsyncExitStack"," and unwinds that stack much later, in reverse order of entry. ",[21,540,542],{"href":541},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F","Yield Dependencies and Cleanup Order"," is the page for the exit half of the story.",[146,545,547],{"id":546},"stage-4-the-path-operation","Stage 4: the path operation",[14,549,550,551,554,555,558,559,562,563,410],{},"Your function finally runs. If it is ",[154,552,553],{},"async def",", it runs directly on the event loop. If it is a plain ",[154,556,557],{},"def",", FastAPI hands it to ",[154,560,561],{},"run_in_threadpool",", which borrows a worker from AnyIO's bounded thread pool — the source of the pathology described in ",[21,564,566],{"href":565},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002F","Fixing Blocking Calls in Async Routes",[14,568,569,570,572],{},"The return value is ",[207,571,209],{}," the response. It is raw material.",[146,574,576],{"id":575},"stage-5-response-model-validation-and-serialization","Stage 5: response model validation and serialization",[14,578,579,580,583],{},"This is the stage people forget exists, and the one that produces the most \"but I already returned the right thing\" confusion. Whatever your handler returned is passed through ",[154,581,582],{},"serialize_response()",", which:",[585,586,587,595,610,617],"ol",{},[588,589,590,591,594],"li",{},"Validates the value against the response field derived from ",[154,592,593],{},"response_model"," or the return annotation, producing a fresh, clean object. This happens even when you returned a Pydantic model already.",[588,596,597,598,30,601,30,604,35,607,410],{},"Applies ",[154,599,600],{},"response_model_exclude_unset",[154,602,603],{},"_exclude_none",[154,605,606],{},"_include",[154,608,609],{},"_exclude",[588,611,612,613,616],{},"Runs ",[154,614,615],{},"jsonable_encoder"," to reduce the result to JSON-compatible primitives.",[588,618,619],{},"Hands those primitives to the response class, which encodes them to bytes.",[14,621,622,623,626,627,30,630,633,634,637,638,35,642,410],{},"The one escape hatch: if your handler returns a ",[154,624,625],{},"Response"," instance (including ",[154,628,629],{},"JSONResponse",[154,631,632],{},"StreamingResponse"," or ",[154,635,636],{},"FileResponse","), FastAPI passes it straight through and none of the above happens. That is a feature, and it is also how people accidentally ship responses that do not match their own OpenAPI schema. Both halves are demonstrated in ",[21,639,641],{"href":640},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002F","Response Model and Serialization Order",[21,643,645],{"href":644},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002F","Streaming and File Responses",[146,647,649],{"id":648},"stage-6-the-unwind","Stage 6: the unwind",[14,651,652,653,656,657,659,660,663],{},"The finished response travels back out. Each middleware's post-",[154,654,655],{},"call_next"," code runs, innermost first, outermost last. ",[154,658,533],{}," dependency teardown and any ",[154,661,662],{},"BackgroundTasks"," you registered are also drained during this window. The precise interleaving between middleware exits and dependency teardown is subtler than the diagrams suggest, and the next section measures it rather than asserting it.",[146,665,667],{"id":666},"where-exception-handlers-actually-sit","Where exception handlers actually sit",[14,669,670],{},"There are two distinct handler mechanisms, and conflating them causes real outages.",[14,672,673,676,677,680,681,684,685,688,689,691,692,35,695,697,698,702],{},[392,674,675],{},"Handlers for specific exception classes"," — anything you register with ",[154,678,679],{},"@app.exception_handler(SomeError)"," where ",[154,682,683],{},"SomeError"," is not literally ",[154,686,687],{},"Exception"," — are stored in ",[154,690,420],{},", the innermost frame. When your handler converts the exception into a response, that response then travels outward through your entire middleware stack normally. Correlation ID headers still get attached. Timing middleware still records the request. ",[154,693,694],{},"HTTPException",[154,696,522],{}," are handled here too, by FastAPI's built-in defaults, which is why ",[21,699,701],{"href":700},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002F","Customising Validation Error Responses"," works by overriding a handler rather than by adding middleware.",[14,704,705,710,711,713,714,716],{},[392,706,707,708],{},"The handler for bare ",[154,709,687],{}," is special-cased. Starlette moves it onto ",[154,712,416],{},", the outermost frame. That means a genuinely unhandled error escapes past every middleware you wrote before anything catches it — so their exit code does not run, and any header they would have added is absent. Worse, ",[154,715,416],{}," always re-raises after sending its 500, so the server can log it.",[14,718,719,720,723,724,728],{},"The practical consequence: if your incident tooling relies on a middleware attaching a trace ID to responses, that ID is missing from precisely the responses you most want to correlate. The fix is to convert errors into responses ",[207,721,722],{},"inside"," the innermost layer — register handlers for the concrete exception types you know about, as ",[21,725,727],{"href":726},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002F","HTTPException vs Custom Exception Classes"," recommends — rather than letting them reach the outer edge.",[146,730,732],{"id":731},"production-implementation-measuring-the-order","Production implementation: measuring the order",[14,734,735,736,739],{},"Assertions about ordering are cheap. Here is an app instrumented to record its own lifecycle, with a ",[154,737,738],{},"\u002Ftrace"," endpoint that reports what happened during the previous request.",[258,741,743],{"className":260,"code":742,"language":262,"meta":263,"style":263},"\"\"\"Trace the real entry\u002Fexit order of ASGI middleware, dependencies and the path operation.\"\"\"\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, FastAPI, Request\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nTRACE: list[str] = []\n\n\ndef log(event: str) -> None:\n    TRACE.append(event)\n\n\nclass Named(BaseHTTPMiddleware):\n    \"\"\"A BaseHTTPMiddleware that records when it is entered and when it is left.\"\"\"\n\n    def __init__(self, app, name: str) -> None:\n        super().__init__(app)\n        self.name = name\n\n    async def dispatch(self, request: Request, call_next):\n        if request.url.path == \"\u002Ftrace\":\n            return await call_next(request)\n        log(f\"middleware {self.name}: enter\")\n        response = await call_next(request)\n        log(f\"middleware {self.name}: exit\")\n        return response\n\n\nclass ResetASGI:\n    \"\"\"Pure ASGI middleware, mounted outermost, that clears the trace per request.\"\"\"\n\n    def __init__(self, app) -> None:\n        self.app = app\n\n    async def __call__(self, scope, receive, send) -> None:\n        if scope[\"type\"] == \"http\" and scope[\"path\"] != \"\u002Ftrace\":\n            TRACE.clear()\n            log(\"asgi outermost: enter\")\n\n            async def send_wrapper(message) -> None:\n                if message[\"type\"] == \"http.response.body\" and not message.get(\"more_body\"):\n                    log(\"asgi outermost: response body sent\")\n                await send(message)\n\n            await self.app(scope, receive, send_wrapper)\n            log(\"asgi outermost: exit\")\n            return\n        await self.app(scope, receive, send)\n\n\napp = FastAPI()\n\n\nasync def app_dep() -> str:\n    log(\"dependency app-level: run\")\n    return \"app\"\n\n\nasync def router_dep() -> str:\n    log(\"dependency router-level: run\")\n    return \"router\"\n\n\nasync def sub_dep() -> str:\n    log(\"dependency sub-dependency: run\")\n    return \"sub\"\n\n\nasync def route_dep(sub: Annotated[str, Depends(sub_dep)]):\n    log(\"dependency route-level: before yield\")\n    try:\n        yield f\"route\u003C-{sub}\"\n    finally:\n        log(\"dependency route-level: after yield (cleanup)\")\n\n\nrouter = APIRouter(dependencies=[Depends(router_dep)])\n\n\n@router.get(\"\u002Fhello\")\nasync def hello(dep: Annotated[str, Depends(route_dep)]) -> dict[str, str]:\n    log(\"path operation: body runs\")\n    return {\"dep\": dep}\n\n\napp.include_router(router, dependencies=[Depends(app_dep)])\n\n\n@app.get(\"\u002Ftrace\")\nasync def trace() -> dict[str, list[str]]:\n    return {\"order\": list(TRACE)}\n\n\n# Added first -> ends up INNERMOST of the three BaseHTTPMiddleware layers.\napp.add_middleware(Named, name=\"added-1st\")\napp.add_middleware(Named, name=\"added-2nd\")\napp.add_middleware(Named, name=\"added-3rd\")\n# Added last -> outermost of everything.\napp.add_middleware(ResetASGI)\n",[154,744,745,751,765,771,783,795,799,819,824,829,852,861,866,871,890,896,901,921,936,950,955,970,987,999,1028,1040,1062,1071,1076,1081,1091,1097,1102,1116,1128,1133,1150,1185,1194,1205,1210,1228,1259,1270,1279,1284,1295,1305,1311,1322,1327,1332,1342,1347,1352,1370,1381,1390,1395,1400,1416,1426,1434,1439,1444,1460,1470,1478,1483,1488,1506,1516,1524,1546,1554,1564,1569,1574,1593,1598,1603,1616,1643,1653,1667,1672,1677,1690,1695,1700,1713,1736,1759,1764,1769,1775,1791,1805,1819,1825],{"__ignoreMap":263},[267,746,747],{"class":269,"line":270},[267,748,750],{"class":749},"sYEJz","\"\"\"Trace the real entry\u002Fexit order of ASGI middleware, dependencies and the path operation.\"\"\"\n",[267,752,753,756,759,762],{"class":269,"line":277},[267,754,755],{"class":284},"from",[267,757,758],{"class":280}," typing ",[267,760,761],{"class":284},"import",[267,763,764],{"class":280}," Annotated\n",[267,766,767],{"class":269,"line":306},[267,768,770],{"emptyLinePlaceholder":769},true,"\n",[267,772,773,775,778,780],{"class":269,"line":319},[267,774,755],{"class":284},[267,776,777],{"class":280}," fastapi ",[267,779,761],{"class":284},[267,781,782],{"class":280}," APIRouter, Depends, FastAPI, Request\n",[267,784,785,787,790,792],{"class":269,"line":340},[267,786,755],{"class":284},[267,788,789],{"class":280}," starlette.middleware.base ",[267,791,761],{"class":284},[267,793,794],{"class":280}," BaseHTTPMiddleware\n",[267,796,797],{"class":269,"line":365},[267,798,770],{"emptyLinePlaceholder":769},[267,800,802,805,808,811,814,816],{"class":269,"line":801},7,[267,803,804],{"class":294},"TRACE",[267,806,807],{"class":280},": list[",[267,809,810],{"class":294},"str",[267,812,813],{"class":280},"] ",[267,815,285],{"class":284},[267,817,818],{"class":280}," []\n",[267,820,822],{"class":269,"line":821},8,[267,823,770],{"emptyLinePlaceholder":769},[267,825,827],{"class":269,"line":826},9,[267,828,770],{"emptyLinePlaceholder":769},[267,830,832,834,838,841,843,846,849],{"class":269,"line":831},10,[267,833,557],{"class":284},[267,835,837],{"class":836},"s3dhs"," log",[267,839,840],{"class":280},"(event: ",[267,842,810],{"class":294},[267,844,845],{"class":280},") -> ",[267,847,848],{"class":294},"None",[267,850,851],{"class":280},":\n",[267,853,855,858],{"class":269,"line":854},11,[267,856,857],{"class":294},"    TRACE",[267,859,860],{"class":280},".append(event)\n",[267,862,864],{"class":269,"line":863},12,[267,865,770],{"emptyLinePlaceholder":769},[267,867,869],{"class":269,"line":868},13,[267,870,770],{"emptyLinePlaceholder":769},[267,872,874,877,881,884,887],{"class":269,"line":873},14,[267,875,876],{"class":284},"class",[267,878,880],{"class":879},"sV4o_"," Named",[267,882,883],{"class":280},"(",[267,885,886],{"class":294},"BaseHTTPMiddleware",[267,888,889],{"class":280},"):\n",[267,891,893],{"class":269,"line":892},15,[267,894,895],{"class":749},"    \"\"\"A BaseHTTPMiddleware that records when it is entered and when it is left.\"\"\"\n",[267,897,899],{"class":269,"line":898},16,[267,900,770],{"emptyLinePlaceholder":769},[267,902,904,907,910,913,915,917,919],{"class":269,"line":903},17,[267,905,906],{"class":284},"    def",[267,908,909],{"class":294}," __init__",[267,911,912],{"class":280},"(self, app, name: ",[267,914,810],{"class":294},[267,916,845],{"class":280},[267,918,848],{"class":294},[267,920,851],{"class":280},[267,922,924,927,930,933],{"class":269,"line":923},18,[267,925,926],{"class":294},"        super",[267,928,929],{"class":280},"().",[267,931,932],{"class":294},"__init__",[267,934,935],{"class":280},"(app)\n",[267,937,939,942,945,947],{"class":269,"line":938},19,[267,940,941],{"class":294},"        self",[267,943,944],{"class":280},".name ",[267,946,285],{"class":284},[267,948,949],{"class":280}," name\n",[267,951,953],{"class":269,"line":952},20,[267,954,770],{"emptyLinePlaceholder":769},[267,956,958,961,964,967],{"class":269,"line":957},21,[267,959,960],{"class":284},"    async",[267,962,963],{"class":284}," def",[267,965,966],{"class":836}," dispatch",[267,968,969],{"class":280},"(self, request: Request, call_next):\n",[267,971,973,976,979,982,985],{"class":269,"line":972},22,[267,974,975],{"class":284},"        if",[267,977,978],{"class":280}," request.url.path ",[267,980,981],{"class":284},"==",[267,983,984],{"class":749}," \"\u002Ftrace\"",[267,986,851],{"class":280},[267,988,990,993,996],{"class":269,"line":989},23,[267,991,992],{"class":284},"            return",[267,994,995],{"class":284}," await",[267,997,998],{"class":280}," call_next(request)\n",[267,1000,1002,1005,1008,1011,1014,1016,1019,1022,1025],{"class":269,"line":1001},24,[267,1003,1004],{"class":280},"        log(",[267,1006,1007],{"class":284},"f",[267,1009,1010],{"class":749},"\"middleware ",[267,1012,1013],{"class":284},"{",[267,1015,368],{"class":294},[267,1017,1018],{"class":280},".name",[267,1020,1021],{"class":284},"}",[267,1023,1024],{"class":749},": enter\"",[267,1026,1027],{"class":280},")\n",[267,1029,1031,1034,1036,1038],{"class":269,"line":1030},25,[267,1032,1033],{"class":280},"        response ",[267,1035,285],{"class":284},[267,1037,995],{"class":284},[267,1039,998],{"class":280},[267,1041,1043,1045,1047,1049,1051,1053,1055,1057,1060],{"class":269,"line":1042},26,[267,1044,1004],{"class":280},[267,1046,1007],{"class":284},[267,1048,1010],{"class":749},[267,1050,1013],{"class":284},[267,1052,368],{"class":294},[267,1054,1018],{"class":280},[267,1056,1021],{"class":284},[267,1058,1059],{"class":749},": exit\"",[267,1061,1027],{"class":280},[267,1063,1065,1068],{"class":269,"line":1064},27,[267,1066,1067],{"class":284},"        return",[267,1069,1070],{"class":280}," response\n",[267,1072,1074],{"class":269,"line":1073},28,[267,1075,770],{"emptyLinePlaceholder":769},[267,1077,1079],{"class":269,"line":1078},29,[267,1080,770],{"emptyLinePlaceholder":769},[267,1082,1084,1086,1089],{"class":269,"line":1083},30,[267,1085,876],{"class":284},[267,1087,1088],{"class":879}," ResetASGI",[267,1090,851],{"class":280},[267,1092,1094],{"class":269,"line":1093},31,[267,1095,1096],{"class":749},"    \"\"\"Pure ASGI middleware, mounted outermost, that clears the trace per request.\"\"\"\n",[267,1098,1100],{"class":269,"line":1099},32,[267,1101,770],{"emptyLinePlaceholder":769},[267,1103,1105,1107,1109,1112,1114],{"class":269,"line":1104},33,[267,1106,906],{"class":284},[267,1108,909],{"class":294},[267,1110,1111],{"class":280},"(self, app) -> ",[267,1113,848],{"class":294},[267,1115,851],{"class":280},[267,1117,1119,1121,1124,1126],{"class":269,"line":1118},34,[267,1120,941],{"class":294},[267,1122,1123],{"class":280},".app ",[267,1125,285],{"class":284},[267,1127,376],{"class":280},[267,1129,1131],{"class":269,"line":1130},35,[267,1132,770],{"emptyLinePlaceholder":769},[267,1134,1136,1138,1140,1143,1146,1148],{"class":269,"line":1135},36,[267,1137,960],{"class":284},[267,1139,963],{"class":284},[267,1141,1142],{"class":294}," __call__",[267,1144,1145],{"class":280},"(self, scope, receive, send) -> ",[267,1147,848],{"class":294},[267,1149,851],{"class":280},[267,1151,1153,1155,1158,1161,1163,1165,1168,1171,1173,1176,1178,1181,1183],{"class":269,"line":1152},37,[267,1154,975],{"class":284},[267,1156,1157],{"class":280}," scope[",[267,1159,1160],{"class":749},"\"type\"",[267,1162,813],{"class":280},[267,1164,981],{"class":284},[267,1166,1167],{"class":749}," \"http\"",[267,1169,1170],{"class":284}," and",[267,1172,1157],{"class":280},[267,1174,1175],{"class":749},"\"path\"",[267,1177,813],{"class":280},[267,1179,1180],{"class":284},"!=",[267,1182,984],{"class":749},[267,1184,851],{"class":280},[267,1186,1188,1191],{"class":269,"line":1187},38,[267,1189,1190],{"class":294},"            TRACE",[267,1192,1193],{"class":280},".clear()\n",[267,1195,1197,1200,1203],{"class":269,"line":1196},39,[267,1198,1199],{"class":280},"            log(",[267,1201,1202],{"class":749},"\"asgi outermost: enter\"",[267,1204,1027],{"class":280},[267,1206,1208],{"class":269,"line":1207},40,[267,1209,770],{"emptyLinePlaceholder":769},[267,1211,1213,1216,1218,1221,1224,1226],{"class":269,"line":1212},41,[267,1214,1215],{"class":284},"            async",[267,1217,963],{"class":284},[267,1219,1220],{"class":836}," send_wrapper",[267,1222,1223],{"class":280},"(message) -> ",[267,1225,848],{"class":294},[267,1227,851],{"class":280},[267,1229,1231,1234,1237,1239,1241,1243,1246,1248,1251,1254,1257],{"class":269,"line":1230},42,[267,1232,1233],{"class":284},"                if",[267,1235,1236],{"class":280}," message[",[267,1238,1160],{"class":749},[267,1240,813],{"class":280},[267,1242,981],{"class":284},[267,1244,1245],{"class":749}," \"http.response.body\"",[267,1247,1170],{"class":284},[267,1249,1250],{"class":284}," not",[267,1252,1253],{"class":280}," message.get(",[267,1255,1256],{"class":749},"\"more_body\"",[267,1258,889],{"class":280},[267,1260,1262,1265,1268],{"class":269,"line":1261},43,[267,1263,1264],{"class":280},"                    log(",[267,1266,1267],{"class":749},"\"asgi outermost: response body sent\"",[267,1269,1027],{"class":280},[267,1271,1273,1276],{"class":269,"line":1272},44,[267,1274,1275],{"class":284},"                await",[267,1277,1278],{"class":280}," send(message)\n",[267,1280,1282],{"class":269,"line":1281},45,[267,1283,770],{"emptyLinePlaceholder":769},[267,1285,1287,1290,1292],{"class":269,"line":1286},46,[267,1288,1289],{"class":284},"            await",[267,1291,295],{"class":294},[267,1293,1294],{"class":280},".app(scope, receive, send_wrapper)\n",[267,1296,1298,1300,1303],{"class":269,"line":1297},47,[267,1299,1199],{"class":280},[267,1301,1302],{"class":749},"\"asgi outermost: exit\"",[267,1304,1027],{"class":280},[267,1306,1308],{"class":269,"line":1307},48,[267,1309,1310],{"class":284},"            return\n",[267,1312,1314,1317,1319],{"class":269,"line":1313},49,[267,1315,1316],{"class":284},"        await",[267,1318,295],{"class":294},[267,1320,1321],{"class":280},".app(scope, receive, send)\n",[267,1323,1325],{"class":269,"line":1324},50,[267,1326,770],{"emptyLinePlaceholder":769},[267,1328,1330],{"class":269,"line":1329},51,[267,1331,770],{"emptyLinePlaceholder":769},[267,1333,1335,1337,1339],{"class":269,"line":1334},52,[267,1336,309],{"class":280},[267,1338,285],{"class":284},[267,1340,1341],{"class":280}," FastAPI()\n",[267,1343,1345],{"class":269,"line":1344},53,[267,1346,770],{"emptyLinePlaceholder":769},[267,1348,1350],{"class":269,"line":1349},54,[267,1351,770],{"emptyLinePlaceholder":769},[267,1353,1355,1358,1360,1363,1366,1368],{"class":269,"line":1354},55,[267,1356,1357],{"class":284},"async",[267,1359,963],{"class":284},[267,1361,1362],{"class":836}," app_dep",[267,1364,1365],{"class":280},"() -> ",[267,1367,810],{"class":294},[267,1369,851],{"class":280},[267,1371,1373,1376,1379],{"class":269,"line":1372},56,[267,1374,1375],{"class":280},"    log(",[267,1377,1378],{"class":749},"\"dependency app-level: run\"",[267,1380,1027],{"class":280},[267,1382,1384,1387],{"class":269,"line":1383},57,[267,1385,1386],{"class":284},"    return",[267,1388,1389],{"class":749}," \"app\"\n",[267,1391,1393],{"class":269,"line":1392},58,[267,1394,770],{"emptyLinePlaceholder":769},[267,1396,1398],{"class":269,"line":1397},59,[267,1399,770],{"emptyLinePlaceholder":769},[267,1401,1403,1405,1407,1410,1412,1414],{"class":269,"line":1402},60,[267,1404,1357],{"class":284},[267,1406,963],{"class":284},[267,1408,1409],{"class":836}," router_dep",[267,1411,1365],{"class":280},[267,1413,810],{"class":294},[267,1415,851],{"class":280},[267,1417,1419,1421,1424],{"class":269,"line":1418},61,[267,1420,1375],{"class":280},[267,1422,1423],{"class":749},"\"dependency router-level: run\"",[267,1425,1027],{"class":280},[267,1427,1429,1431],{"class":269,"line":1428},62,[267,1430,1386],{"class":284},[267,1432,1433],{"class":749}," \"router\"\n",[267,1435,1437],{"class":269,"line":1436},63,[267,1438,770],{"emptyLinePlaceholder":769},[267,1440,1442],{"class":269,"line":1441},64,[267,1443,770],{"emptyLinePlaceholder":769},[267,1445,1447,1449,1451,1454,1456,1458],{"class":269,"line":1446},65,[267,1448,1357],{"class":284},[267,1450,963],{"class":284},[267,1452,1453],{"class":836}," sub_dep",[267,1455,1365],{"class":280},[267,1457,810],{"class":294},[267,1459,851],{"class":280},[267,1461,1463,1465,1468],{"class":269,"line":1462},66,[267,1464,1375],{"class":280},[267,1466,1467],{"class":749},"\"dependency sub-dependency: run\"",[267,1469,1027],{"class":280},[267,1471,1473,1475],{"class":269,"line":1472},67,[267,1474,1386],{"class":284},[267,1476,1477],{"class":749}," \"sub\"\n",[267,1479,1481],{"class":269,"line":1480},68,[267,1482,770],{"emptyLinePlaceholder":769},[267,1484,1486],{"class":269,"line":1485},69,[267,1487,770],{"emptyLinePlaceholder":769},[267,1489,1491,1493,1495,1498,1501,1503],{"class":269,"line":1490},70,[267,1492,1357],{"class":284},[267,1494,963],{"class":284},[267,1496,1497],{"class":836}," route_dep",[267,1499,1500],{"class":280},"(sub: Annotated[",[267,1502,810],{"class":294},[267,1504,1505],{"class":280},", Depends(sub_dep)]):\n",[267,1507,1509,1511,1514],{"class":269,"line":1508},71,[267,1510,1375],{"class":280},[267,1512,1513],{"class":749},"\"dependency route-level: before yield\"",[267,1515,1027],{"class":280},[267,1517,1519,1522],{"class":269,"line":1518},72,[267,1520,1521],{"class":284},"    try",[267,1523,851],{"class":280},[267,1525,1527,1530,1533,1536,1538,1541,1543],{"class":269,"line":1526},73,[267,1528,1529],{"class":284},"        yield",[267,1531,1532],{"class":284}," f",[267,1534,1535],{"class":749},"\"route\u003C-",[267,1537,1013],{"class":284},[267,1539,1540],{"class":280},"sub",[267,1542,1021],{"class":284},[267,1544,1545],{"class":749},"\"\n",[267,1547,1549,1552],{"class":269,"line":1548},74,[267,1550,1551],{"class":284},"    finally",[267,1553,851],{"class":280},[267,1555,1557,1559,1562],{"class":269,"line":1556},75,[267,1558,1004],{"class":280},[267,1560,1561],{"class":749},"\"dependency route-level: after yield (cleanup)\"",[267,1563,1027],{"class":280},[267,1565,1567],{"class":269,"line":1566},76,[267,1568,770],{"emptyLinePlaceholder":769},[267,1570,1572],{"class":269,"line":1571},77,[267,1573,770],{"emptyLinePlaceholder":769},[267,1575,1577,1580,1582,1585,1588,1590],{"class":269,"line":1576},78,[267,1578,1579],{"class":280},"router ",[267,1581,285],{"class":284},[267,1583,1584],{"class":280}," APIRouter(",[267,1586,1587],{"class":879},"dependencies",[267,1589,285],{"class":284},[267,1591,1592],{"class":280},"[Depends(router_dep)])\n",[267,1594,1596],{"class":269,"line":1595},79,[267,1597,770],{"emptyLinePlaceholder":769},[267,1599,1601],{"class":269,"line":1600},80,[267,1602,770],{"emptyLinePlaceholder":769},[267,1604,1606,1609,1611,1614],{"class":269,"line":1605},81,[267,1607,1608],{"class":836},"@router.get",[267,1610,883],{"class":280},[267,1612,1613],{"class":749},"\"\u002Fhello\"",[267,1615,1027],{"class":280},[267,1617,1619,1621,1623,1626,1629,1631,1634,1636,1638,1640],{"class":269,"line":1618},82,[267,1620,1357],{"class":284},[267,1622,963],{"class":284},[267,1624,1625],{"class":836}," hello",[267,1627,1628],{"class":280},"(dep: Annotated[",[267,1630,810],{"class":294},[267,1632,1633],{"class":280},", Depends(route_dep)]) -> dict[",[267,1635,810],{"class":294},[267,1637,30],{"class":280},[267,1639,810],{"class":294},[267,1641,1642],{"class":280},"]:\n",[267,1644,1646,1648,1651],{"class":269,"line":1645},83,[267,1647,1375],{"class":280},[267,1649,1650],{"class":749},"\"path operation: body runs\"",[267,1652,1027],{"class":280},[267,1654,1656,1658,1661,1664],{"class":269,"line":1655},84,[267,1657,1386],{"class":284},[267,1659,1660],{"class":280}," {",[267,1662,1663],{"class":749},"\"dep\"",[267,1665,1666],{"class":280},": dep}\n",[267,1668,1670],{"class":269,"line":1669},85,[267,1671,770],{"emptyLinePlaceholder":769},[267,1673,1675],{"class":269,"line":1674},86,[267,1676,770],{"emptyLinePlaceholder":769},[267,1678,1680,1683,1685,1687],{"class":269,"line":1679},87,[267,1681,1682],{"class":280},"app.include_router(router, ",[267,1684,1587],{"class":879},[267,1686,285],{"class":284},[267,1688,1689],{"class":280},"[Depends(app_dep)])\n",[267,1691,1693],{"class":269,"line":1692},88,[267,1694,770],{"emptyLinePlaceholder":769},[267,1696,1698],{"class":269,"line":1697},89,[267,1699,770],{"emptyLinePlaceholder":769},[267,1701,1703,1706,1708,1711],{"class":269,"line":1702},90,[267,1704,1705],{"class":836},"@app.get",[267,1707,883],{"class":280},[267,1709,1710],{"class":749},"\"\u002Ftrace\"",[267,1712,1027],{"class":280},[267,1714,1716,1718,1720,1723,1726,1728,1731,1733],{"class":269,"line":1715},91,[267,1717,1357],{"class":284},[267,1719,963],{"class":284},[267,1721,1722],{"class":836}," trace",[267,1724,1725],{"class":280},"() -> dict[",[267,1727,810],{"class":294},[267,1729,1730],{"class":280},", list[",[267,1732,810],{"class":294},[267,1734,1735],{"class":280},"]]:\n",[267,1737,1739,1741,1743,1746,1749,1752,1754,1756],{"class":269,"line":1738},92,[267,1740,1386],{"class":284},[267,1742,1660],{"class":280},[267,1744,1745],{"class":749},"\"order\"",[267,1747,1748],{"class":280},": ",[267,1750,1751],{"class":294},"list",[267,1753,883],{"class":280},[267,1755,804],{"class":294},[267,1757,1758],{"class":280},")}\n",[267,1760,1762],{"class":269,"line":1761},93,[267,1763,770],{"emptyLinePlaceholder":769},[267,1765,1767],{"class":269,"line":1766},94,[267,1768,770],{"emptyLinePlaceholder":769},[267,1770,1772],{"class":269,"line":1771},95,[267,1773,1774],{"class":273},"# Added first -> ends up INNERMOST of the three BaseHTTPMiddleware layers.\n",[267,1776,1778,1781,1784,1786,1789],{"class":269,"line":1777},96,[267,1779,1780],{"class":280},"app.add_middleware(Named, ",[267,1782,1783],{"class":879},"name",[267,1785,285],{"class":284},[267,1787,1788],{"class":749},"\"added-1st\"",[267,1790,1027],{"class":280},[267,1792,1794,1796,1798,1800,1803],{"class":269,"line":1793},97,[267,1795,1780],{"class":280},[267,1797,1783],{"class":879},[267,1799,285],{"class":284},[267,1801,1802],{"class":749},"\"added-2nd\"",[267,1804,1027],{"class":280},[267,1806,1808,1810,1812,1814,1817],{"class":269,"line":1807},98,[267,1809,1780],{"class":280},[267,1811,1783],{"class":879},[267,1813,285],{"class":284},[267,1815,1816],{"class":749},"\"added-3rd\"",[267,1818,1027],{"class":280},[267,1820,1822],{"class":269,"line":1821},99,[267,1823,1824],{"class":273},"# Added last -> outermost of everything.\n",[267,1826,1828],{"class":269,"line":1827},100,[267,1829,1830],{"class":280},"app.add_middleware(ResetASGI)\n",[14,1832,1833,1834,1837],{},"Real output, captured by running this app in-process (",[154,1835,1836],{},"_verify\u002Fexamples\u002Farch-lifecycle-order.py","):",[258,1839,1843],{"className":1840,"code":1842,"language":67,"meta":263},[1841],"language-text","$ GET \u002Fhello\n200 OK\n{\n  \"dep\": \"route\u003C-sub\"\n}\n\n$ GET \u002Ftrace\n200 OK\n{\n  \"order\": [\n    \"asgi outermost: enter\",\n    \"middleware added-3rd: enter\",\n    \"middleware added-2nd: enter\",\n    \"middleware added-1st: enter\",\n    \"dependency app-level: run\",\n    \"dependency router-level: run\",\n    \"dependency sub-dependency: run\",\n    \"dependency route-level: before yield\",\n    \"path operation: body runs\",\n    \"middleware added-1st: exit\",\n    \"middleware added-2nd: exit\",\n    \"dependency route-level: after yield (cleanup)\",\n    \"middleware added-3rd: exit\",\n    \"asgi outermost: response body sent\",\n    \"asgi outermost: exit\"\n  ]\n}\n",[154,1844,1842],{"__ignoreMap":263},[14,1846,1847,1848,1851,1852,1855],{},"Read the entry half top to bottom: registration order ",[154,1849,1850],{},"1st, 2nd, 3rd"," produced execution order ",[154,1853,1854],{},"3rd, 2nd, 1st",". The inversion is not folklore. Dependencies then resolve outermost-declaration-first — application, then router, then route — with sub-dependencies ahead of the parents that need them.",[14,1857,1858,1859,1861,1862,1864,1865,1868,1869,35,1872,1875,1876,1878,1879,1881,1882,1885],{},"The exit half contains the genuinely surprising result. The ",[154,1860,533],{}," teardown does ",[392,1863,209],{}," run neatly after all middleware or neatly before them; it lands ",[207,1866,1867],{},"between"," ",[154,1870,1871],{},"added-2nd",[154,1873,1874],{},"added-3rd",". That is a scheduling artefact of ",[154,1877,886],{},", which returns a streaming response from ",[154,1880,655],{},": the inner layers resume as soon as the response headers are available, while teardown is driven by the exhaustion of the body stream. The reliable guarantees are narrower than a diagram implies: teardown runs after the path operation, and it completes before the outermost ASGI layer returns. ",[392,1883,1884],{},"Do not build logic that depends on teardown happening before or after any particular middleware."," If you need a strict ordering, own the resource inside the layer that must see it closed.",[146,1887,1889],{"id":1888},"production-implementation-proving-where-handlers-sit","Production implementation: proving where handlers sit",[14,1891,1892,1893,1895,1896,1899,1900,410],{},"The second example separates the two handler mechanisms empirically. It has an ordinary ",[154,1894,886],{},", a pure-ASGI middleware registered last (so it is the outermost ",[207,1897,1898],{},"user"," frame), and a wrapper around the whole ASGI app — which is the only way to observe from outside ",[154,1901,416],{},[258,1903,1905],{"className":260,"code":1904,"language":262,"meta":263,"style":263},"\"\"\"Where exception handlers sit relative to user middleware, proven by what still runs.\"\"\"\nfrom fastapi import Depends, FastAPI, HTTPException, Request\nfrom fastapi.responses import JSONResponse\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nTRACE: list[str] = []\n\n\nclass QuotaExceeded(Exception):\n    def __init__(self, remaining: int) -> None:\n        self.remaining = remaining\n\n\nclass Recorder(BaseHTTPMiddleware):\n    \"\"\"An ordinary user middleware: does its exit work only if nothing escapes past it.\"\"\"\n\n    async def dispatch(self, request: Request, call_next):\n        if request.url.path == \"\u002Ftrace\":\n            return await call_next(request)\n        TRACE.append(\"user middleware: enter\")\n        response = await call_next(request)\n        TRACE.append(\"user middleware: exit ran\")\n        response.headers[\"x-touched-by-middleware\"] = \"yes\"\n        return response\n\n\nclass OutermostUserMiddleware:\n    \"\"\"Pure ASGI, added last, so it is the outermost *user* middleware.\"\"\"\n\n    def __init__(self, app) -> None:\n        self.app = app\n\n    async def __call__(self, scope, receive, send) -> None:\n        if scope[\"type\"] != \"http\" or scope[\"path\"] == \"\u002Ftrace\":\n            await self.app(scope, receive, send)\n            return\n        TRACE.clear()\n        TRACE.append(\"outermost user middleware: enter\")\n        try:\n            await self.app(scope, receive, send)\n        except Exception as exc:\n            TRACE.append(f\"outermost user middleware: {type(exc).__name__} escaped past it\")\n            raise\n        TRACE.append(\"outermost user middleware: exit ran\")\n\n\napi = FastAPI()\n\n\n@api.exception_handler(QuotaExceeded)\nasync def quota_handler(request: Request, exc: QuotaExceeded) -> JSONResponse:\n    TRACE.append(\"QuotaExceeded handler ran\")\n    return JSONResponse(status_code=429, content={\"error\": \"quota\", \"remaining\": exc.remaining})\n\n\nasync def require_token(x_token: str | None = None) -> str:\n    if x_token != \"secret\":\n        TRACE.append(\"dependency raised HTTPException\")\n        raise HTTPException(status_code=401, detail=\"bad token\")\n    return x_token\n\n\n@api.get(\"\u002Fguarded\")\nasync def guarded(token: str = Depends(require_token)) -> dict[str, str]:\n    return {\"token\": token}\n\n\n@api.get(\"\u002Fquota\")\nasync def quota() -> dict[str, str]:\n    TRACE.append(\"path operation raised QuotaExceeded\")\n    raise QuotaExceeded(remaining=0)\n\n\n@api.get(\"\u002Fboom\")\nasync def boom() -> dict[str, str]:\n    TRACE.append(\"path operation raised RuntimeError\")\n    raise RuntimeError(\"nothing handles me\")\n\n\n@api.get(\"\u002Ftrace\")\nasync def trace() -> dict[str, list[str]]:\n    return {\"order\": list(TRACE)}\n\n\napi.add_middleware(Recorder)\napi.add_middleware(OutermostUserMiddleware)\n\n\nclass BeyondStarlette:\n    \"\"\"Wraps the whole ASGI app, i.e. outside Starlette's own ServerErrorMiddleware.\n\n    ServerErrorMiddleware always re-raises after sending its 500, so a real server can log\n    it. This wrapper records that re-raise and swallows it so the transcript can be captured.\n    \"\"\"\n\n    def __init__(self, app) -> None:\n        self.app = app\n\n    async def __call__(self, scope, receive, send) -> None:\n        try:\n            await self.app(scope, receive, send)\n        except Exception as exc:\n            TRACE.append(f\"outside ServerErrorMiddleware: {type(exc).__name__} re-raised to server\")\n\n\napp = BeyondStarlette(api)\n",[154,1906,1907,1912,1923,1935,1945,1949,1963,1967,1971,1984,2002,2014,2018,2022,2035,2040,2044,2054,2066,2074,2087,2097,2108,2123,2129,2133,2137,2146,2151,2155,2167,2177,2181,2195,2224,2232,2236,2242,2253,2260,2268,2282,2310,2315,2326,2330,2334,2343,2347,2351,2359,2371,2382,2422,2426,2430,2461,2476,2487,2514,2521,2525,2529,2541,2568,2580,2584,2588,2599,2618,2629,2647,2651,2655,2666,2685,2696,2710,2714,2718,2728,2746,2764,2768,2772,2777,2782,2786,2790,2799,2804,2808,2813,2818,2823,2827,2839,2849,2853,2867,2873,2882,2893,2920,2925,2930],{"__ignoreMap":263},[267,1908,1909],{"class":269,"line":270},[267,1910,1911],{"class":749},"\"\"\"Where exception handlers sit relative to user middleware, proven by what still runs.\"\"\"\n",[267,1913,1914,1916,1918,1920],{"class":269,"line":277},[267,1915,755],{"class":284},[267,1917,777],{"class":280},[267,1919,761],{"class":284},[267,1921,1922],{"class":280}," Depends, FastAPI, HTTPException, Request\n",[267,1924,1925,1927,1930,1932],{"class":269,"line":306},[267,1926,755],{"class":284},[267,1928,1929],{"class":280}," fastapi.responses ",[267,1931,761],{"class":284},[267,1933,1934],{"class":280}," JSONResponse\n",[267,1936,1937,1939,1941,1943],{"class":269,"line":319},[267,1938,755],{"class":284},[267,1940,789],{"class":280},[267,1942,761],{"class":284},[267,1944,794],{"class":280},[267,1946,1947],{"class":269,"line":340},[267,1948,770],{"emptyLinePlaceholder":769},[267,1950,1951,1953,1955,1957,1959,1961],{"class":269,"line":365},[267,1952,804],{"class":294},[267,1954,807],{"class":280},[267,1956,810],{"class":294},[267,1958,813],{"class":280},[267,1960,285],{"class":284},[267,1962,818],{"class":280},[267,1964,1965],{"class":269,"line":801},[267,1966,770],{"emptyLinePlaceholder":769},[267,1968,1969],{"class":269,"line":821},[267,1970,770],{"emptyLinePlaceholder":769},[267,1972,1973,1975,1978,1980,1982],{"class":269,"line":826},[267,1974,876],{"class":284},[267,1976,1977],{"class":879}," QuotaExceeded",[267,1979,883],{"class":280},[267,1981,687],{"class":294},[267,1983,889],{"class":280},[267,1985,1986,1988,1990,1993,1996,1998,2000],{"class":269,"line":831},[267,1987,906],{"class":284},[267,1989,909],{"class":294},[267,1991,1992],{"class":280},"(self, remaining: ",[267,1994,1995],{"class":294},"int",[267,1997,845],{"class":280},[267,1999,848],{"class":294},[267,2001,851],{"class":280},[267,2003,2004,2006,2009,2011],{"class":269,"line":854},[267,2005,941],{"class":294},[267,2007,2008],{"class":280},".remaining ",[267,2010,285],{"class":284},[267,2012,2013],{"class":280}," remaining\n",[267,2015,2016],{"class":269,"line":863},[267,2017,770],{"emptyLinePlaceholder":769},[267,2019,2020],{"class":269,"line":868},[267,2021,770],{"emptyLinePlaceholder":769},[267,2023,2024,2026,2029,2031,2033],{"class":269,"line":873},[267,2025,876],{"class":284},[267,2027,2028],{"class":879}," Recorder",[267,2030,883],{"class":280},[267,2032,886],{"class":294},[267,2034,889],{"class":280},[267,2036,2037],{"class":269,"line":892},[267,2038,2039],{"class":749},"    \"\"\"An ordinary user middleware: does its exit work only if nothing escapes past it.\"\"\"\n",[267,2041,2042],{"class":269,"line":898},[267,2043,770],{"emptyLinePlaceholder":769},[267,2045,2046,2048,2050,2052],{"class":269,"line":903},[267,2047,960],{"class":284},[267,2049,963],{"class":284},[267,2051,966],{"class":836},[267,2053,969],{"class":280},[267,2055,2056,2058,2060,2062,2064],{"class":269,"line":923},[267,2057,975],{"class":284},[267,2059,978],{"class":280},[267,2061,981],{"class":284},[267,2063,984],{"class":749},[267,2065,851],{"class":280},[267,2067,2068,2070,2072],{"class":269,"line":938},[267,2069,992],{"class":284},[267,2071,995],{"class":284},[267,2073,998],{"class":280},[267,2075,2076,2079,2082,2085],{"class":269,"line":952},[267,2077,2078],{"class":294},"        TRACE",[267,2080,2081],{"class":280},".append(",[267,2083,2084],{"class":749},"\"user middleware: enter\"",[267,2086,1027],{"class":280},[267,2088,2089,2091,2093,2095],{"class":269,"line":957},[267,2090,1033],{"class":280},[267,2092,285],{"class":284},[267,2094,995],{"class":284},[267,2096,998],{"class":280},[267,2098,2099,2101,2103,2106],{"class":269,"line":972},[267,2100,2078],{"class":294},[267,2102,2081],{"class":280},[267,2104,2105],{"class":749},"\"user middleware: exit ran\"",[267,2107,1027],{"class":280},[267,2109,2110,2113,2116,2118,2120],{"class":269,"line":989},[267,2111,2112],{"class":280},"        response.headers[",[267,2114,2115],{"class":749},"\"x-touched-by-middleware\"",[267,2117,813],{"class":280},[267,2119,285],{"class":284},[267,2121,2122],{"class":749}," \"yes\"\n",[267,2124,2125,2127],{"class":269,"line":1001},[267,2126,1067],{"class":284},[267,2128,1070],{"class":280},[267,2130,2131],{"class":269,"line":1030},[267,2132,770],{"emptyLinePlaceholder":769},[267,2134,2135],{"class":269,"line":1042},[267,2136,770],{"emptyLinePlaceholder":769},[267,2138,2139,2141,2144],{"class":269,"line":1064},[267,2140,876],{"class":284},[267,2142,2143],{"class":879}," OutermostUserMiddleware",[267,2145,851],{"class":280},[267,2147,2148],{"class":269,"line":1073},[267,2149,2150],{"class":749},"    \"\"\"Pure ASGI, added last, so it is the outermost *user* middleware.\"\"\"\n",[267,2152,2153],{"class":269,"line":1078},[267,2154,770],{"emptyLinePlaceholder":769},[267,2156,2157,2159,2161,2163,2165],{"class":269,"line":1083},[267,2158,906],{"class":284},[267,2160,909],{"class":294},[267,2162,1111],{"class":280},[267,2164,848],{"class":294},[267,2166,851],{"class":280},[267,2168,2169,2171,2173,2175],{"class":269,"line":1093},[267,2170,941],{"class":294},[267,2172,1123],{"class":280},[267,2174,285],{"class":284},[267,2176,376],{"class":280},[267,2178,2179],{"class":269,"line":1099},[267,2180,770],{"emptyLinePlaceholder":769},[267,2182,2183,2185,2187,2189,2191,2193],{"class":269,"line":1104},[267,2184,960],{"class":284},[267,2186,963],{"class":284},[267,2188,1142],{"class":294},[267,2190,1145],{"class":280},[267,2192,848],{"class":294},[267,2194,851],{"class":280},[267,2196,2197,2199,2201,2203,2205,2207,2209,2212,2214,2216,2218,2220,2222],{"class":269,"line":1118},[267,2198,975],{"class":284},[267,2200,1157],{"class":280},[267,2202,1160],{"class":749},[267,2204,813],{"class":280},[267,2206,1180],{"class":284},[267,2208,1167],{"class":749},[267,2210,2211],{"class":284}," or",[267,2213,1157],{"class":280},[267,2215,1175],{"class":749},[267,2217,813],{"class":280},[267,2219,981],{"class":284},[267,2221,984],{"class":749},[267,2223,851],{"class":280},[267,2225,2226,2228,2230],{"class":269,"line":1130},[267,2227,1289],{"class":284},[267,2229,295],{"class":294},[267,2231,1321],{"class":280},[267,2233,2234],{"class":269,"line":1135},[267,2235,1310],{"class":284},[267,2237,2238,2240],{"class":269,"line":1152},[267,2239,2078],{"class":294},[267,2241,1193],{"class":280},[267,2243,2244,2246,2248,2251],{"class":269,"line":1187},[267,2245,2078],{"class":294},[267,2247,2081],{"class":280},[267,2249,2250],{"class":749},"\"outermost user middleware: enter\"",[267,2252,1027],{"class":280},[267,2254,2255,2258],{"class":269,"line":1196},[267,2256,2257],{"class":284},"        try",[267,2259,851],{"class":280},[267,2261,2262,2264,2266],{"class":269,"line":1207},[267,2263,1289],{"class":284},[267,2265,295],{"class":294},[267,2267,1321],{"class":280},[267,2269,2270,2273,2276,2279],{"class":269,"line":1212},[267,2271,2272],{"class":284},"        except",[267,2274,2275],{"class":294}," Exception",[267,2277,2278],{"class":284}," as",[267,2280,2281],{"class":280}," exc:\n",[267,2283,2284,2286,2288,2290,2293,2295,2297,2300,2303,2305,2308],{"class":269,"line":1230},[267,2285,1190],{"class":294},[267,2287,2081],{"class":280},[267,2289,1007],{"class":284},[267,2291,2292],{"class":749},"\"outermost user middleware: ",[267,2294,1013],{"class":284},[267,2296,175],{"class":294},[267,2298,2299],{"class":280},"(exc).",[267,2301,2302],{"class":294},"__name__",[267,2304,1021],{"class":284},[267,2306,2307],{"class":749}," escaped past it\"",[267,2309,1027],{"class":280},[267,2311,2312],{"class":269,"line":1261},[267,2313,2314],{"class":284},"            raise\n",[267,2316,2317,2319,2321,2324],{"class":269,"line":1272},[267,2318,2078],{"class":294},[267,2320,2081],{"class":280},[267,2322,2323],{"class":749},"\"outermost user middleware: exit ran\"",[267,2325,1027],{"class":280},[267,2327,2328],{"class":269,"line":1281},[267,2329,770],{"emptyLinePlaceholder":769},[267,2331,2332],{"class":269,"line":1286},[267,2333,770],{"emptyLinePlaceholder":769},[267,2335,2336,2339,2341],{"class":269,"line":1297},[267,2337,2338],{"class":280},"api ",[267,2340,285],{"class":284},[267,2342,1341],{"class":280},[267,2344,2345],{"class":269,"line":1307},[267,2346,770],{"emptyLinePlaceholder":769},[267,2348,2349],{"class":269,"line":1313},[267,2350,770],{"emptyLinePlaceholder":769},[267,2352,2353,2356],{"class":269,"line":1324},[267,2354,2355],{"class":836},"@api.exception_handler",[267,2357,2358],{"class":280},"(QuotaExceeded)\n",[267,2360,2361,2363,2365,2368],{"class":269,"line":1329},[267,2362,1357],{"class":284},[267,2364,963],{"class":284},[267,2366,2367],{"class":836}," quota_handler",[267,2369,2370],{"class":280},"(request: Request, exc: QuotaExceeded) -> JSONResponse:\n",[267,2372,2373,2375,2377,2380],{"class":269,"line":1334},[267,2374,857],{"class":294},[267,2376,2081],{"class":280},[267,2378,2379],{"class":749},"\"QuotaExceeded handler ran\"",[267,2381,1027],{"class":280},[267,2383,2384,2386,2389,2392,2394,2397,2399,2402,2404,2406,2409,2411,2414,2416,2419],{"class":269,"line":1344},[267,2385,1386],{"class":284},[267,2387,2388],{"class":280}," JSONResponse(",[267,2390,2391],{"class":879},"status_code",[267,2393,285],{"class":284},[267,2395,2396],{"class":294},"429",[267,2398,30],{"class":280},[267,2400,2401],{"class":879},"content",[267,2403,285],{"class":284},[267,2405,1013],{"class":280},[267,2407,2408],{"class":749},"\"error\"",[267,2410,1748],{"class":280},[267,2412,2413],{"class":749},"\"quota\"",[267,2415,30],{"class":280},[267,2417,2418],{"class":749},"\"remaining\"",[267,2420,2421],{"class":280},": exc.remaining})\n",[267,2423,2424],{"class":269,"line":1349},[267,2425,770],{"emptyLinePlaceholder":769},[267,2427,2428],{"class":269,"line":1354},[267,2429,770],{"emptyLinePlaceholder":769},[267,2431,2432,2434,2436,2439,2442,2444,2447,2450,2453,2455,2457,2459],{"class":269,"line":1372},[267,2433,1357],{"class":284},[267,2435,963],{"class":284},[267,2437,2438],{"class":836}," require_token",[267,2440,2441],{"class":280},"(x_token: ",[267,2443,810],{"class":294},[267,2445,2446],{"class":284}," |",[267,2448,2449],{"class":294}," None",[267,2451,2452],{"class":284}," =",[267,2454,2449],{"class":294},[267,2456,845],{"class":280},[267,2458,810],{"class":294},[267,2460,851],{"class":280},[267,2462,2463,2466,2469,2471,2474],{"class":269,"line":1383},[267,2464,2465],{"class":284},"    if",[267,2467,2468],{"class":280}," x_token ",[267,2470,1180],{"class":284},[267,2472,2473],{"class":749}," \"secret\"",[267,2475,851],{"class":280},[267,2477,2478,2480,2482,2485],{"class":269,"line":1392},[267,2479,2078],{"class":294},[267,2481,2081],{"class":280},[267,2483,2484],{"class":749},"\"dependency raised HTTPException\"",[267,2486,1027],{"class":280},[267,2488,2489,2492,2495,2497,2499,2502,2504,2507,2509,2512],{"class":269,"line":1397},[267,2490,2491],{"class":284},"        raise",[267,2493,2494],{"class":280}," HTTPException(",[267,2496,2391],{"class":879},[267,2498,285],{"class":284},[267,2500,2501],{"class":294},"401",[267,2503,30],{"class":280},[267,2505,2506],{"class":879},"detail",[267,2508,285],{"class":284},[267,2510,2511],{"class":749},"\"bad token\"",[267,2513,1027],{"class":280},[267,2515,2516,2518],{"class":269,"line":1402},[267,2517,1386],{"class":284},[267,2519,2520],{"class":280}," x_token\n",[267,2522,2523],{"class":269,"line":1418},[267,2524,770],{"emptyLinePlaceholder":769},[267,2526,2527],{"class":269,"line":1428},[267,2528,770],{"emptyLinePlaceholder":769},[267,2530,2531,2534,2536,2539],{"class":269,"line":1436},[267,2532,2533],{"class":836},"@api.get",[267,2535,883],{"class":280},[267,2537,2538],{"class":749},"\"\u002Fguarded\"",[267,2540,1027],{"class":280},[267,2542,2543,2545,2547,2550,2553,2555,2557,2560,2562,2564,2566],{"class":269,"line":1441},[267,2544,1357],{"class":284},[267,2546,963],{"class":284},[267,2548,2549],{"class":836}," guarded",[267,2551,2552],{"class":280},"(token: ",[267,2554,810],{"class":294},[267,2556,2452],{"class":284},[267,2558,2559],{"class":280}," Depends(require_token)) -> dict[",[267,2561,810],{"class":294},[267,2563,30],{"class":280},[267,2565,810],{"class":294},[267,2567,1642],{"class":280},[267,2569,2570,2572,2574,2577],{"class":269,"line":1446},[267,2571,1386],{"class":284},[267,2573,1660],{"class":280},[267,2575,2576],{"class":749},"\"token\"",[267,2578,2579],{"class":280},": token}\n",[267,2581,2582],{"class":269,"line":1462},[267,2583,770],{"emptyLinePlaceholder":769},[267,2585,2586],{"class":269,"line":1472},[267,2587,770],{"emptyLinePlaceholder":769},[267,2589,2590,2592,2594,2597],{"class":269,"line":1480},[267,2591,2533],{"class":836},[267,2593,883],{"class":280},[267,2595,2596],{"class":749},"\"\u002Fquota\"",[267,2598,1027],{"class":280},[267,2600,2601,2603,2605,2608,2610,2612,2614,2616],{"class":269,"line":1485},[267,2602,1357],{"class":284},[267,2604,963],{"class":284},[267,2606,2607],{"class":836}," quota",[267,2609,1725],{"class":280},[267,2611,810],{"class":294},[267,2613,30],{"class":280},[267,2615,810],{"class":294},[267,2617,1642],{"class":280},[267,2619,2620,2622,2624,2627],{"class":269,"line":1490},[267,2621,857],{"class":294},[267,2623,2081],{"class":280},[267,2625,2626],{"class":749},"\"path operation raised QuotaExceeded\"",[267,2628,1027],{"class":280},[267,2630,2631,2634,2637,2640,2642,2645],{"class":269,"line":1508},[267,2632,2633],{"class":284},"    raise",[267,2635,2636],{"class":280}," QuotaExceeded(",[267,2638,2639],{"class":879},"remaining",[267,2641,285],{"class":284},[267,2643,2644],{"class":294},"0",[267,2646,1027],{"class":280},[267,2648,2649],{"class":269,"line":1518},[267,2650,770],{"emptyLinePlaceholder":769},[267,2652,2653],{"class":269,"line":1526},[267,2654,770],{"emptyLinePlaceholder":769},[267,2656,2657,2659,2661,2664],{"class":269,"line":1548},[267,2658,2533],{"class":836},[267,2660,883],{"class":280},[267,2662,2663],{"class":749},"\"\u002Fboom\"",[267,2665,1027],{"class":280},[267,2667,2668,2670,2672,2675,2677,2679,2681,2683],{"class":269,"line":1556},[267,2669,1357],{"class":284},[267,2671,963],{"class":284},[267,2673,2674],{"class":836}," boom",[267,2676,1725],{"class":280},[267,2678,810],{"class":294},[267,2680,30],{"class":280},[267,2682,810],{"class":294},[267,2684,1642],{"class":280},[267,2686,2687,2689,2691,2694],{"class":269,"line":1566},[267,2688,857],{"class":294},[267,2690,2081],{"class":280},[267,2692,2693],{"class":749},"\"path operation raised RuntimeError\"",[267,2695,1027],{"class":280},[267,2697,2698,2700,2703,2705,2708],{"class":269,"line":1571},[267,2699,2633],{"class":284},[267,2701,2702],{"class":294}," RuntimeError",[267,2704,883],{"class":280},[267,2706,2707],{"class":749},"\"nothing handles me\"",[267,2709,1027],{"class":280},[267,2711,2712],{"class":269,"line":1576},[267,2713,770],{"emptyLinePlaceholder":769},[267,2715,2716],{"class":269,"line":1595},[267,2717,770],{"emptyLinePlaceholder":769},[267,2719,2720,2722,2724,2726],{"class":269,"line":1600},[267,2721,2533],{"class":836},[267,2723,883],{"class":280},[267,2725,1710],{"class":749},[267,2727,1027],{"class":280},[267,2729,2730,2732,2734,2736,2738,2740,2742,2744],{"class":269,"line":1605},[267,2731,1357],{"class":284},[267,2733,963],{"class":284},[267,2735,1722],{"class":836},[267,2737,1725],{"class":280},[267,2739,810],{"class":294},[267,2741,1730],{"class":280},[267,2743,810],{"class":294},[267,2745,1735],{"class":280},[267,2747,2748,2750,2752,2754,2756,2758,2760,2762],{"class":269,"line":1618},[267,2749,1386],{"class":284},[267,2751,1660],{"class":280},[267,2753,1745],{"class":749},[267,2755,1748],{"class":280},[267,2757,1751],{"class":294},[267,2759,883],{"class":280},[267,2761,804],{"class":294},[267,2763,1758],{"class":280},[267,2765,2766],{"class":269,"line":1645},[267,2767,770],{"emptyLinePlaceholder":769},[267,2769,2770],{"class":269,"line":1655},[267,2771,770],{"emptyLinePlaceholder":769},[267,2773,2774],{"class":269,"line":1669},[267,2775,2776],{"class":280},"api.add_middleware(Recorder)\n",[267,2778,2779],{"class":269,"line":1674},[267,2780,2781],{"class":280},"api.add_middleware(OutermostUserMiddleware)\n",[267,2783,2784],{"class":269,"line":1679},[267,2785,770],{"emptyLinePlaceholder":769},[267,2787,2788],{"class":269,"line":1692},[267,2789,770],{"emptyLinePlaceholder":769},[267,2791,2792,2794,2797],{"class":269,"line":1697},[267,2793,876],{"class":284},[267,2795,2796],{"class":879}," BeyondStarlette",[267,2798,851],{"class":280},[267,2800,2801],{"class":269,"line":1702},[267,2802,2803],{"class":749},"    \"\"\"Wraps the whole ASGI app, i.e. outside Starlette's own ServerErrorMiddleware.\n",[267,2805,2806],{"class":269,"line":1715},[267,2807,770],{"emptyLinePlaceholder":769},[267,2809,2810],{"class":269,"line":1738},[267,2811,2812],{"class":749},"    ServerErrorMiddleware always re-raises after sending its 500, so a real server can log\n",[267,2814,2815],{"class":269,"line":1761},[267,2816,2817],{"class":749},"    it. This wrapper records that re-raise and swallows it so the transcript can be captured.\n",[267,2819,2820],{"class":269,"line":1766},[267,2821,2822],{"class":749},"    \"\"\"\n",[267,2824,2825],{"class":269,"line":1771},[267,2826,770],{"emptyLinePlaceholder":769},[267,2828,2829,2831,2833,2835,2837],{"class":269,"line":1777},[267,2830,906],{"class":284},[267,2832,909],{"class":294},[267,2834,1111],{"class":280},[267,2836,848],{"class":294},[267,2838,851],{"class":280},[267,2840,2841,2843,2845,2847],{"class":269,"line":1793},[267,2842,941],{"class":294},[267,2844,1123],{"class":280},[267,2846,285],{"class":284},[267,2848,376],{"class":280},[267,2850,2851],{"class":269,"line":1807},[267,2852,770],{"emptyLinePlaceholder":769},[267,2854,2855,2857,2859,2861,2863,2865],{"class":269,"line":1821},[267,2856,960],{"class":284},[267,2858,963],{"class":284},[267,2860,1142],{"class":294},[267,2862,1145],{"class":280},[267,2864,848],{"class":294},[267,2866,851],{"class":280},[267,2868,2869,2871],{"class":269,"line":1827},[267,2870,2257],{"class":284},[267,2872,851],{"class":280},[267,2874,2876,2878,2880],{"class":269,"line":2875},101,[267,2877,1289],{"class":284},[267,2879,295],{"class":294},[267,2881,1321],{"class":280},[267,2883,2885,2887,2889,2891],{"class":269,"line":2884},102,[267,2886,2272],{"class":284},[267,2888,2275],{"class":294},[267,2890,2278],{"class":284},[267,2892,2281],{"class":280},[267,2894,2896,2898,2900,2902,2905,2907,2909,2911,2913,2915,2918],{"class":269,"line":2895},103,[267,2897,1190],{"class":294},[267,2899,2081],{"class":280},[267,2901,1007],{"class":284},[267,2903,2904],{"class":749},"\"outside ServerErrorMiddleware: ",[267,2906,1013],{"class":284},[267,2908,175],{"class":294},[267,2910,2299],{"class":280},[267,2912,2302],{"class":294},[267,2914,1021],{"class":284},[267,2916,2917],{"class":749}," re-raised to server\"",[267,2919,1027],{"class":280},[267,2921,2923],{"class":269,"line":2922},104,[267,2924,770],{"emptyLinePlaceholder":769},[267,2926,2928],{"class":269,"line":2927},105,[267,2929,770],{"emptyLinePlaceholder":769},[267,2931,2933,2935,2937],{"class":269,"line":2932},106,[267,2934,309],{"class":280},[267,2936,285],{"class":284},[267,2938,2939],{"class":280}," BeyondStarlette(api)\n",[14,2941,2942,2943,2946],{},"Real output from ",[154,2944,2945],{},"_verify\u002Fexamples\u002Farch-lifecycle-exceptions.py",":",[258,2948,2951],{"className":2949,"code":2950,"language":67,"meta":263},[1841],"$ GET \u002Fguarded\n401 Unauthorized\n{\n  \"detail\": \"bad token\"\n}\n\n$ GET \u002Ftrace\n200 OK\n{\n  \"order\": [\n    \"outermost user middleware: enter\",\n    \"user middleware: enter\",\n    \"dependency raised HTTPException\",\n    \"user middleware: exit ran\",\n    \"outermost user middleware: exit ran\"\n  ]\n}\n\n$ GET \u002Fquota\n429 Too Many Requests\n{\n  \"error\": \"quota\",\n  \"remaining\": 0\n}\n\n$ GET \u002Ftrace\n200 OK\n{\n  \"order\": [\n    \"outermost user middleware: enter\",\n    \"user middleware: enter\",\n    \"path operation raised QuotaExceeded\",\n    \"QuotaExceeded handler ran\",\n    \"user middleware: exit ran\",\n    \"outermost user middleware: exit ran\"\n  ]\n}\n\n$ GET \u002Fboom\n500 Internal Server Error\nInternal Server Error\n\n$ GET \u002Ftrace\n200 OK\n{\n  \"order\": [\n    \"outermost user middleware: enter\",\n    \"user middleware: enter\",\n    \"path operation raised RuntimeError\",\n    \"outermost user middleware: RuntimeError escaped past it\",\n    \"outside ServerErrorMiddleware: RuntimeError re-raised to server\"\n  ]\n}\n",[154,2952,2950],{"__ignoreMap":263},[14,2954,2955,2956,2958,2959,2962,2963,2966,2967,2970,2971,2974,2975,2978,2979,2981,2982,410],{},"Three requests, three distinct shapes. The ",[154,2957,694],{}," from a dependency and the custom ",[154,2960,2961],{},"QuotaExceeded"," from the handler both produce responses that travel back out through the full middleware stack — ",[154,2964,2965],{},"user middleware: exit ran"," appears in both, so the ",[154,2968,2969],{},"x-touched-by-middleware"," header is present on those error responses. The ",[154,2972,2973],{},"RuntimeError"," does not: it blows straight past both middleware layers, and the only thing that observes it is the wrapper sitting outside Starlette entirely. Note also the body of that 500 — a bare ",[154,2976,2977],{},"Internal Server Error"," in plain text, not JSON, because it was produced by ",[154,2980,416],{}," and never touched your envelope logic. Building a consistent envelope that survives this is the subject of ",[21,2983,2985],{"href":2984},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002F","Global Exception Handlers for Consistent API Responses",[146,2987,2989],{"id":2988},"async-and-performance-notes","Async and performance notes",[14,2991,2992],{},"Every layer in the diagram costs something on every request, and the cost is not uniform.",[14,2994,2995,2997,2998,3000,3001,3003,3004,3007,3008,3011],{},[154,2996,886],{}," is the expensive one. It spawns an AnyIO task group per request and pipes the response through a memory object stream so that ",[154,2999,655],{}," can hand you a ",[154,3002,625],{}," object. That is convenient, and it is why the teardown interleaving above is untidy. A pure ASGI middleware — a class with ",[154,3005,3006],{},"__call__(self, scope, receive, send)"," that wraps ",[154,3009,3010],{},"send"," — adds close to nothing and never buffers. For anything on the hot path, and for anything that must not break streaming, write pure ASGI.",[14,3013,3014,3015,3017],{},"Dependency resolution is cheap but not free, and it is linear in the size of the graph. The graph is built at import time, so the per-request cost is the solving, not the introspection. Where it does become visible is ",[154,3016,557],{}," dependencies: each one costs a thread pool hop, and a route with four sync dependencies makes four hops before your handler starts.",[14,3019,3020,3021,3025,3026,3030],{},"Serialization is frequently the single largest slice of a JSON endpoint's CPU time, especially for list responses of nested models. That cost is real and is measured in ",[21,3022,3024],{"href":3023},"\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002F","Pydantic Model Serialization Performance","; ",[21,3027,3029],{"href":3028},"\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002F","Model Construct: When to Skip Validation"," covers the case where you can prove the data is already valid.",[146,3032,3034],{"id":3033},"testing-strategy","Testing strategy",[14,3036,3037,3038,410],{},"Lifecycle behaviour is testable, and it is worth testing because it changes under you when someone reorders ",[154,3039,404],{},[14,3041,3042,3043,3046],{},"Assert ordering by instrumenting, exactly as the examples above do: keep a module-level list, append from each layer, and assert on the list rather than on prose. ",[154,3044,3045],{},"TestClient"," drives the full stack including middleware, so the ordering you observe in a test is the ordering you get in production.",[14,3048,3049,3050,3052],{},"Assert that error responses still carry your cross-cutting headers. A test that requests a route which raises a handled exception and asserts the correlation header is present will catch the day someone converts a specific handler into a bare ",[154,3051,687],{}," handler and silently moves it outside the middleware stack.",[14,3054,3055,3056,3059,3060,3064],{},"Use ",[154,3057,3058],{},"app.dependency_overrides"," to replace expensive providers without disturbing the graph shape — the override is substituted during resolution, so ordering and caching behaviour are preserved. ",[21,3061,3063],{"href":3062},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","Overriding Dependencies in Tests"," covers the pitfalls, chiefly forgetting to clear the dict between tests.",[146,3066,3068],{"id":3067},"failure-modes-and-diagnosis","Failure modes and diagnosis",[3070,3071,3072,3084,3095,3104,3113,3126,3136],"ul",{},[588,3073,3074,3077,3078,3080,3081,3083],{},[392,3075,3076],{},"A header is missing from 500s but present on 200s."," The error escaped to ",[154,3079,416],{},". Register a handler for the concrete exception type so it is caught in ",[154,3082,420],{}," instead.",[588,3085,3086,3091,3092,3094],{},[392,3087,3088,3089,410],{},"Middleware cannot see ",[154,3090,454],{}," It runs before routing. Read the scope after ",[154,3093,655],{},", or move the logic to a dependency.",[588,3096,3097,3100,3101,3103],{},[392,3098,3099],{},"The handler gets an empty body."," A middleware awaited ",[154,3102,213],{}," and did not replay the message. Either buffer and re-inject, or stop reading bodies in middleware.",[588,3105,3106,3112],{},[392,3107,3108,3109,3111],{},"A ",[154,3110,533],{}," dependency's resource is closed too early or too late."," Do not reason about it relative to middleware; see the interleaving above. Scope the resource to the code that uses it.",[588,3114,3115,1868,3118,3121,3122,410],{},[392,3116,3117],{},"CORS headers absent on error responses.",[154,3119,3120],{},"CORSMiddleware"," must be registered late enough to be outside the layer producing the response; the specifics are in ",[21,3123,3125],{"href":3124},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002F","CORS Middleware Configuration",[588,3127,3128,3131,3132,3135],{},[392,3129,3130],{},"A dependency runs twice."," It is declared at two sites with different callables, or ",[154,3133,3134],{},"use_cache=False"," is set somewhere.",[588,3137,3138,3141,3142,3146],{},[392,3139,3140],{},"Background tasks never run."," They are attached to the response and drained during the unwind; if a middleware replaces the response object, the tasks go with it. ",[21,3143,3145],{"href":3144},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002F","When BackgroundTasks Silently Fails"," enumerates the ways.",[146,3148,3150],{"id":3149},"choosing-a-layer","Choosing a layer",[3152,3153,3154,3170],"table",{},[3155,3156,3157],"thead",{},[3158,3159,3160,3164,3167],"tr",{},[3161,3162,3163],"th",{},"Concern",[3161,3165,3166],{},"Right layer",[3161,3168,3169],{},"Why",[3171,3172,3173,3185,3196,3207,3220,3231,3243,3257],"tbody",{},[3158,3174,3175,3179,3182],{},[3176,3177,3178],"td",{},"Correlation ID, timing, request logging",[3176,3180,3181],{},"Pure ASGI middleware, registered last",[3176,3183,3184],{},"Must wrap everything, must not buffer bodies",[3158,3186,3187,3190,3193],{},[3176,3188,3189],{},"CORS, GZip, HTTPS redirect",[3176,3191,3192],{},"Starlette's built-in middleware",[3176,3194,3195],{},"Protocol-level, applies before routing",[3158,3197,3198,3201,3204],{},[3176,3199,3200],{},"Authentication and authorization",[3176,3202,3203],{},"Dependency",[3176,3205,3206],{},"Needs parsed inputs and per-route scoping",[3158,3208,3209,3212,3217],{},[3176,3210,3211],{},"Database session, HTTP client",[3176,3213,3214,3216],{},[154,3215,533],{}," dependency",[3176,3218,3219],{},"Deterministic setup and teardown per request",[3158,3221,3222,3225,3228],{},[3176,3223,3224],{},"Converting a domain error to a status code",[3176,3226,3227],{},"Exception handler for that class",[3176,3229,3230],{},"Stays inside the middleware stack",[3158,3232,3233,3236,3240],{},[3176,3234,3235],{},"Shaping the response body",[3176,3237,3238],{},[154,3239,593],{},[3176,3241,3242],{},"Validated, documented in OpenAPI",[3158,3244,3245,3248,3252],{},[3176,3246,3247],{},"Sending large or unbounded payloads",[3176,3249,3250],{},[154,3251,632],{},[3176,3253,3254,3255],{},"Bypasses buffering and ",[154,3256,593],{},[3158,3258,3259,3262,3265],{},[3176,3260,3261],{},"Connection pools, warm caches",[3176,3263,3264],{},"Lifespan",[3176,3266,3267],{},"Once per process, not per request",[14,3269,3270,3271,3275],{},"The lifespan row is the one people place wrongly most often; ",[21,3272,3274],{"href":3273},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002F","Lifespan Events vs Startup\u002FShutdown"," explains why the ASGI lifespan protocol runs on a different scope type entirely and never touches the request path.",[146,3277,3279],{"id":3278},"faq","FAQ",[14,3281,3282,3285,3286,3288,3289,3291],{},[392,3283,3284],{},"Why does middleware added later run before middleware added earlier?","\nBecause ",[154,3287,385],{}," inserts at the front of the middleware list and Starlette builds the stack by wrapping from the innermost outward. Each ",[154,3290,385],{}," call wraps everything registered so far, so the last one registered ends up as the outermost wrapper and therefore runs first on the way in and last on the way out.",[14,3293,3294,3297,3298,3300,3301,3303,3304,3306],{},[392,3295,3296],{},"Where do exception handlers run relative to my middleware?","\nHandlers you register for specific exception classes run inside ",[154,3299,420],{},", which sits at the innermost edge of the stack, just outside the router. Your middleware therefore still sees the response those handlers produce. The handler for the bare ",[154,3302,687],{}," class is different: it is installed on ",[154,3305,416],{},", which is outside all user middleware, so your middleware's exit code never runs for a truly unhandled error.",[14,3308,3309,3315],{},[392,3310,3311,3312,3314],{},"Does ",[154,3313,593],{}," validation happen before or after middleware sees the response?","\nBefore. Serialization and response model validation happen inside the path operation layer, well within the router. By the time any middleware inspects the response, the body has already been validated, filtered and encoded to JSON bytes.",[14,3317,3318,3321],{},[392,3319,3320],{},"Is the request body read before my dependencies run?","\nOnly if something asks for it. FastAPI resolves the dependency graph first and reads and parses the body as part of solving the path operation's own parameters. A dependency that only inspects headers runs without the body ever being awaited.",[14,3323,3324,3327,3328,633,3330,3332,3333,3335],{},[392,3325,3326],{},"Why can middleware not see the matched route or path parameters?","\nStandard user middleware runs before routing, so at that moment the scope has no ",[154,3329,240],{},[154,3331,244],{}," entry. Starlette's router populates those during matching, which happens inside ",[154,3334,420],{},". Middleware that needs the matched route must read the scope after calling the next layer, or the logic belongs in a dependency instead.",[146,3337,3339],{"id":3338},"related-reading","Related Reading",[3070,3341,3342,3350,3360,3371,3379],{},[588,3343,3344,1868,3347,3349],{},[392,3345,3346],{},"Up to the section:",[21,3348,24],{"href":23}," for how the pieces fit into an application's structure.",[588,3351,3352,1868,3355,3359],{},[392,3353,3354],{},"A single request, traced end to end:",[21,3356,3358],{"href":3357},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002F","How a Request Flows Through FastAPI"," settles the ordering question with measurements rather than diagrams.",[588,3361,3362,1868,3365,3367,3368,3370],{},[392,3363,3364],{},"The serialization stage in depth:",[21,3366,641],{"href":640}," explains what ",[154,3369,593],{}," really does to your return value.",[588,3372,3373,1868,3376,3378],{},[392,3374,3375],{},"When the response is not a body:",[21,3377,645],{"href":644}," covers the path that skips serialization entirely.",[588,3380,3381,1868,3384,35,3386,410],{},[392,3382,3383],{},"Neighbouring layers:",[21,3385,29],{"href":28},[21,3387,34],{"href":33},[3389,3390,3391],"style",{},"html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}",{"title":263,"searchDepth":277,"depth":277,"links":3393},[3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410],{"id":148,"depth":277,"text":149},{"id":164,"depth":277,"text":165},{"id":248,"depth":277,"text":249},{"id":424,"depth":277,"text":425},{"id":484,"depth":277,"text":485},{"id":546,"depth":277,"text":547},{"id":575,"depth":277,"text":576},{"id":648,"depth":277,"text":649},{"id":666,"depth":277,"text":667},{"id":731,"depth":277,"text":732},{"id":1888,"depth":277,"text":1889},{"id":2988,"depth":277,"text":2989},{"id":3033,"depth":277,"text":3034},{"id":3067,"depth":277,"text":3068},{"id":3149,"depth":277,"text":3150},{"id":3278,"depth":277,"text":3279},{"id":3338,"depth":277,"text":3339},"2026-07-20","Trace a FastAPI request end to end: ASGI scope, the Starlette middleware stack, routing, dependency resolution, response model serialization and the unwind.","md",[3415,3417,3419,3422,3424],{"q":3284,"a":3416},"Because add_middleware inserts at the front of the middleware list and Starlette builds the stack by wrapping from the innermost outward. Each add_middleware call wraps everything registered so far, so the last one registered ends up as the outermost wrapper and therefore runs first on the way in and last on the way out.",{"q":3296,"a":3418},"Handlers you register for specific exception classes run inside ExceptionMiddleware, which sits at the innermost edge of the stack, just outside the router. Your middleware therefore still sees the response those handlers produce. The handler for the bare Exception class is different: it is installed on ServerErrorMiddleware, which is outside all user middleware, so your middleware's exit code never runs for a truly unhandled error.",{"q":3420,"a":3421},"Does response_model validation happen before or after middleware sees the response?","Before. Serialization and response model validation happen inside the path operation layer, well within the router. By the time any middleware inspects the response, the body has already been validated, filtered and encoded to JSON bytes.",{"q":3320,"a":3423},"Only if something asks for it. FastAPI resolves the dependency graph first and reads and parses the body as part of solving the path operation's own parameters. A dependency that only inspects headers runs without the body ever being awaited.",{"q":3326,"a":3425},"Standard user middleware runs before routing, so at that moment the scope has no route or path_params entry. Starlette's router populates those during matching, which happens inside ExceptionMiddleware. Middleware that needs the matched route must read scope after calling the next layer, or the logic belongs in a dependency instead.",null,{"slug":3428,"breadcrumb":3429},"request-response-lifecycle",[3430,3433,3435],{"label":3431,"path":3432},"Home","\u002F",{"label":3434,"path":23},"Core Architecture & Routing Patterns",{"label":3436,"path":3437},"Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002F","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle",{"title":5,"description":3412},"core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex","topic","8R6ZnPfbMRFydz1EKloFqUgL3N_X4OTmwKIHA_M4jNw",[3444,3626,3826],{"title":3445,"path":3446,"stem":3447,"children":3448,"page":-1},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[3449,3452,3482,3506,3530,3554,3590,3614],{"title":3450,"path":3446,"stem":3451},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":3453,"path":3454,"stem":3455,"children":3456,"page":-1},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[3457,3458,3464,3470,3476],{"title":3453,"path":3454,"stem":3455},{"title":3459,"path":3460,"stem":3461,"children":3462},"Before, After and Wrap Validators in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators\u002Findex",[3463],{"title":3459,"path":3460,"stem":3461},{"title":3465,"path":3466,"stem":3467,"children":3468},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[3469],{"title":3465,"path":3466,"stem":3467},{"title":3471,"path":3472,"stem":3473,"children":3474},"Cross-Field Validation Patterns in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002Findex",[3475],{"title":3471,"path":3472,"stem":3473},{"title":3477,"path":3478,"stem":3479,"children":3480},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[3481],{"title":3477,"path":3478,"stem":3479},{"title":3483,"path":3484,"stem":3485,"children":3486,"page":-1},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[3487,3488,3494,3500],{"title":3483,"path":3484,"stem":3485},{"title":3489,"path":3490,"stem":3491,"children":3492},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[3493],{"title":3489,"path":3490,"stem":3491},{"title":3495,"path":3496,"stem":3497,"children":3498},"Discriminated Unions in OpenAPI with Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi\u002Findex",[3499],{"title":3495,"path":3496,"stem":3497},{"title":3501,"path":3502,"stem":3503,"children":3504},"Examples in the OpenAPI Schema with FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema\u002Findex",[3505],{"title":3501,"path":3502,"stem":3503},{"title":3507,"path":3508,"stem":3509,"children":3510,"page":-1},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[3511,3512,3518,3524],{"title":3507,"path":3508,"stem":3509},{"title":3513,"path":3514,"stem":3515,"children":3516},"Excluding Fields Per Endpoint in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002Findex",[3517],{"title":3513,"path":3514,"stem":3515},{"title":3519,"path":3520,"stem":3521,"children":3522},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[3523],{"title":3519,"path":3520,"stem":3521},{"title":3525,"path":3526,"stem":3527,"children":3528},"Self-Referencing and Recursive Models in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex",[3529],{"title":3525,"path":3526,"stem":3527},{"title":3531,"path":3532,"stem":3533,"children":3534},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[3535,3536,3542,3548],{"title":3531,"path":3532,"stem":3533},{"title":3537,"path":3538,"stem":3539,"children":3540},"model_construct and When to Skip Validation in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002Findex",[3541],{"title":3537,"path":3538,"stem":3539},{"title":3543,"path":3544,"stem":3545,"children":3546},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[3547],{"title":3543,"path":3544,"stem":3545},{"title":3549,"path":3550,"stem":3551,"children":3552},"TypeAdapter for Non-Model Types in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types\u002Findex",[3553],{"title":3549,"path":3550,"stem":3551},{"title":3555,"path":3556,"stem":3557,"children":3558},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[3559,3560,3566,3572,3578,3584],{"title":3555,"path":3556,"stem":3557},{"title":3561,"path":3562,"stem":3563,"children":3564},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[3565],{"title":3561,"path":3562,"stem":3563},{"title":3567,"path":3568,"stem":3569,"children":3570},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[3571],{"title":3567,"path":3568,"stem":3569},{"title":3573,"path":3574,"stem":3575,"children":3576},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[3577],{"title":3573,"path":3574,"stem":3575},{"title":3579,"path":3580,"stem":3581,"children":3582},"Replacing json_encoders with field_serializer in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer\u002Findex",[3583],{"title":3579,"path":3580,"stem":3581},{"title":3585,"path":3586,"stem":3587,"children":3588},"Migrating @root_validator to @model_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator\u002Findex",[3589],{"title":3585,"path":3586,"stem":3587},{"title":3591,"path":3592,"stem":3593,"children":3594},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[3595,3596,3602,3608],{"title":3591,"path":3592,"stem":3593},{"title":3597,"path":3598,"stem":3599,"children":3600},"Optional vs Nullable Fields in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002Findex",[3601],{"title":3597,"path":3598,"stem":3599},{"title":3603,"path":3604,"stem":3605,"children":3606},"Query, Path and Body Parameter Validation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002Findex",[3607],{"title":3603,"path":3604,"stem":3605},{"title":3609,"path":3610,"stem":3611,"children":3612},"Validating File Uploads and Forms in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002Findex",[3613],{"title":3609,"path":3610,"stem":3611},{"title":3615,"path":3616,"stem":3617,"children":3618,"page":-1},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[3619,3620],{"title":3615,"path":3616,"stem":3617},{"title":3621,"path":3622,"stem":3623,"children":3624},"Annotated Dependencies and Reusable Types in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex",[3625],{"title":3621,"path":3622,"stem":3623},{"title":3627,"path":3628,"stem":3629,"children":3630,"page":-1},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[3631,3634,3664,3694,3724,3748,3778,3802],{"title":3632,"path":3628,"stem":3633},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":3635,"path":3636,"stem":3637,"children":3638,"page":-1},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[3639,3640,3646,3652,3658],{"title":3635,"path":3636,"stem":3637},{"title":3641,"path":3642,"stem":3643,"children":3644},"Concurrent Requests with asyncio.gather in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002Findex",[3645],{"title":3641,"path":3642,"stem":3643},{"title":3647,"path":3648,"stem":3649,"children":3650},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[3651],{"title":3647,"path":3648,"stem":3649},{"title":3653,"path":3654,"stem":3655,"children":3656},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[3657],{"title":3653,"path":3654,"stem":3655},{"title":3659,"path":3660,"stem":3661,"children":3662},"Running Sync Code in a Threadpool in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002Findex",[3663],{"title":3659,"path":3660,"stem":3661},{"title":3665,"path":3666,"stem":3667,"children":3668,"page":-1},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[3669,3670,3676,3682,3688],{"title":3665,"path":3666,"stem":3667},{"title":3671,"path":3672,"stem":3673,"children":3674},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[3675],{"title":3671,"path":3672,"stem":3673},{"title":3677,"path":3678,"stem":3679,"children":3680},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[3681],{"title":3677,"path":3678,"stem":3679},{"title":3683,"path":3684,"stem":3685,"children":3686},"Testing with Async Database Fixtures in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002Findex",[3687],{"title":3683,"path":3684,"stem":3685},{"title":3689,"path":3690,"stem":3691,"children":3692},"Transaction Management and Rollback in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002Findex",[3693],{"title":3689,"path":3690,"stem":3691},{"title":3695,"path":3696,"stem":3697,"children":3698,"page":-1},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[3699,3700,3706,3712,3718],{"title":3695,"path":3696,"stem":3697},{"title":3701,"path":3702,"stem":3703,"children":3704},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[3705],{"title":3701,"path":3702,"stem":3703},{"title":3707,"path":3708,"stem":3709,"children":3710},"Retry and Idempotency for FastAPI Background Tasks","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks","async-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks\u002Findex",[3711],{"title":3707,"path":3708,"stem":3709},{"title":3713,"path":3714,"stem":3715,"children":3716},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[3717],{"title":3713,"path":3714,"stem":3715},{"title":3719,"path":3720,"stem":3721,"children":3722},"When FastAPI BackgroundTasks Silently Fails","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails","async-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002Findex",[3723],{"title":3719,"path":3720,"stem":3721},{"title":3725,"path":3726,"stem":3727,"children":3728,"page":-1},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[3729,3730,3736,3742],{"title":3725,"path":3726,"stem":3727},{"title":3731,"path":3732,"stem":3733,"children":3734},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[3735],{"title":3731,"path":3732,"stem":3733},{"title":3737,"path":3738,"stem":3739,"children":3740},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[3741],{"title":3737,"path":3738,"stem":3739},{"title":3743,"path":3744,"stem":3745,"children":3746},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[3747],{"title":3743,"path":3744,"stem":3745},{"title":3749,"path":3750,"stem":3751,"children":3752,"page":-1},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[3753,3754,3760,3766,3772],{"title":3749,"path":3750,"stem":3751},{"title":3755,"path":3756,"stem":3757,"children":3758},"Correlating Logs, Traces and Errors in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors\u002Findex",[3759],{"title":3755,"path":3756,"stem":3757},{"title":3761,"path":3762,"stem":3763,"children":3764},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[3765],{"title":3761,"path":3762,"stem":3763},{"title":3767,"path":3768,"stem":3769,"children":3770},"Prometheus Metrics for FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi\u002Findex",[3771],{"title":3767,"path":3768,"stem":3769},{"title":3773,"path":3774,"stem":3775,"children":3776},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[3777],{"title":3773,"path":3774,"stem":3775},{"title":3779,"path":3780,"stem":3781,"children":3782,"page":-1},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[3783,3784,3790,3796],{"title":3779,"path":3780,"stem":3781},{"title":3785,"path":3786,"stem":3787,"children":3788},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[3789],{"title":3785,"path":3786,"stem":3787},{"title":3791,"path":3792,"stem":3793,"children":3794},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[3795],{"title":3791,"path":3792,"stem":3793},{"title":3797,"path":3798,"stem":3799,"children":3800},"Rate Limit Headers and 429 Responses in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses","async-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses\u002Findex",[3801],{"title":3797,"path":3798,"stem":3799},{"title":3803,"path":3804,"stem":3805,"children":3806},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[3807,3808,3814,3820],{"title":3803,"path":3804,"stem":3805},{"title":3809,"path":3810,"stem":3811,"children":3812},"Mocking External Services in FastAPI Tests","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002Findex",[3813],{"title":3809,"path":3810,"stem":3811},{"title":3815,"path":3816,"stem":3817,"children":3818},"TestClient vs httpx AsyncClient in FastAPI","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002Findex",[3819],{"title":3815,"path":3816,"stem":3817},{"title":3821,"path":3822,"stem":3823,"children":3824},"Testing Async FastAPI Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex",[3825],{"title":3821,"path":3822,"stem":3823},{"title":3827,"path":3828,"stem":3829,"children":3830,"page":-1},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[3831,3834,3852,3876,3912,3935,3964,3994],{"title":3832,"path":3828,"stem":3833},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":3835,"path":3836,"stem":3837,"children":3838,"page":-1},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[3839,3840,3846],{"title":3835,"path":3836,"stem":3837},{"title":3841,"path":3842,"stem":3843,"children":3844},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[3845],{"title":3841,"path":3842,"stem":3843},{"title":3847,"path":3848,"stem":3849,"children":3850},"Lifespan Events vs Startup and Shutdown in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex",[3851],{"title":3847,"path":3848,"stem":3849},{"title":3853,"path":3854,"stem":3855,"children":3856},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[3857,3858,3864,3870],{"title":3853,"path":3854,"stem":3855},{"title":3859,"path":3860,"stem":3861,"children":3862},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[3863],{"title":3859,"path":3860,"stem":3861},{"title":3865,"path":3866,"stem":3867,"children":3868},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[3869],{"title":3865,"path":3866,"stem":3867},{"title":3871,"path":3872,"stem":3873,"children":3874},"Secrets and .env Files Per Environment in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002Findex",[3875],{"title":3871,"path":3872,"stem":3873},{"title":3877,"path":3878,"stem":3879,"children":3880,"page":-1},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[3881,3882,3888,3894,3900,3906],{"title":3877,"path":3878,"stem":3879},{"title":3883,"path":3884,"stem":3885,"children":3886},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[3887],{"title":3883,"path":3884,"stem":3885},{"title":3889,"path":3890,"stem":3891,"children":3892},"Dependency Caching and use_cache in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002Findex",[3893],{"title":3889,"path":3890,"stem":3891},{"title":3895,"path":3896,"stem":3897,"children":3898},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[3899],{"title":3895,"path":3896,"stem":3897},{"title":3901,"path":3902,"stem":3903,"children":3904},"Overriding Dependencies in FastAPI Tests","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002Findex",[3905],{"title":3901,"path":3902,"stem":3903},{"title":3907,"path":3908,"stem":3909,"children":3910},"Yield Dependencies and Cleanup Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex",[3911],{"title":3907,"path":3908,"stem":3909},{"title":3913,"path":3914,"stem":3915,"children":3916,"page":-1},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[3917,3918,3924,3929],{"title":3913,"path":3914,"stem":3915},{"title":3919,"path":3920,"stem":3921,"children":3922},"Customising Validation Error Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002Findex",[3923],{"title":3919,"path":3920,"stem":3921},{"title":2985,"path":3925,"stem":3926,"children":3927},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[3928],{"title":2985,"path":3925,"stem":3926},{"title":3930,"path":3931,"stem":3932,"children":3933},"HTTPException vs Custom Exception Classes in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002Findex",[3934],{"title":3930,"path":3931,"stem":3932},{"title":3936,"path":3937,"stem":3938,"children":3939,"page":-1},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[3940,3941,3947,3953,3959],{"title":3936,"path":3937,"stem":3938},{"title":3942,"path":3943,"stem":3944,"children":3945},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[3946],{"title":3942,"path":3943,"stem":3944},{"title":3948,"path":3949,"stem":3950,"children":3951},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[3952],{"title":3948,"path":3949,"stem":3950},{"title":3954,"path":3955,"stem":3956,"children":3957},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[3958],{"title":3954,"path":3955,"stem":3956},{"title":229,"path":3960,"stem":3961,"children":3962},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex",[3963],{"title":229,"path":3960,"stem":3961},{"title":3965,"path":3966,"stem":3967,"children":3968,"page":-1},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[3969,3970,3976,3982,3988],{"title":3965,"path":3966,"stem":3967},{"title":3971,"path":3972,"stem":3973,"children":3974},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[3975],{"title":3971,"path":3972,"stem":3973},{"title":3977,"path":3978,"stem":3979,"children":3980},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[3981],{"title":3977,"path":3978,"stem":3979},{"title":3983,"path":3984,"stem":3985,"children":3986},"Router Tags and OpenAPI Grouping in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping\u002Findex",[3987],{"title":3983,"path":3984,"stem":3985},{"title":3989,"path":3990,"stem":3991,"children":3992},"Versioning APIs with FastAPI Routers","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers\u002Findex",[3993],{"title":3989,"path":3990,"stem":3991},{"title":5,"path":3438,"stem":3440,"children":3995,"page":-1},[3996,3997,4002,4007],{"title":5,"path":3438,"stem":3440},{"title":3358,"path":3998,"stem":3999,"children":4000},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex",[4001],{"title":3358,"path":3998,"stem":3999},{"title":641,"path":4003,"stem":4004,"children":4005},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002Findex",[4006],{"title":641,"path":4003,"stem":4004},{"title":4008,"path":4009,"stem":4010,"children":4011},"Streaming and File Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002Findex",[4012],{"title":4008,"path":4009,"stem":4010},[3426,3426],1784588202410]