[{"data":1,"prerenderedAt":2744},["ShallowReactive",2],{"page-\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002F":3,"nav":2169,"surround-\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002F":2743},{"id":4,"title":5,"body":6,"dateModified":2138,"datePublished":2138,"description":2139,"extension":2140,"faq":2141,"howto":2154,"meta":2155,"navigation":305,"path":2164,"seo":2165,"stem":2166,"type":2167,"__hash__":2168},"content\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex.md","Testing FastAPI Applications",{"type":7,"value":8,"toc":2124},"minimark",[9,13,22,36,145,150,180,184,204,207,229,232,241,270,274,392,515,518,620,623,629,653,754,761,765,771,789,1044,1047,1053,1083,1086,1142,1156,1160,1163,1188,1195,1202,1224,1231,1235,1245,1457,1463,1470,1485,1489,1495,1501,1508,1511,1517,1550,1553,1635,1639,1646,1653,1737,1748,1759,1852,1859,1870,1874,1877,1880,1901,1916,1920,1930,1956,1966,1978,1984,1993,1999,2003,2021,2027,2039,2048,2057,2063,2067,2120],[10,11,5],"h1",{"id":12},"testing-fastapi-applications",[14,15,16,17,21],"p",{},"Testing a FastAPI application well means testing it ",[18,19,20],"em",{},"through"," the framework. Routing, dependency resolution, request validation and response serialization are where most production bugs live, and none of them run when you call a path operation function directly. This guide covers the client choice, the fixture patterns, and the dividing line between tests that need the app and tests that do not.",[14,23,24,25,30,31,35],{},"This is part of ",[26,27,29],"a",{"href":28},"\u002Fasync-background-tasks-observability\u002F","Async, Background Tasks and Observability",". It leans heavily on ",[26,32,34],{"href":33},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002F","Async Correctness and Concurrency",", because the difference between the two test clients is entirely a question of which event loop runs your app.",[37,38,39,141],"figure",{},[40,41,49,50,49,54,49,58,49,65,49,74,49,80,49,88,49,93,49,96,49,100,49,105,49,110,49,113,49,118,49,121,49,123,49,125,49,128,49,130,49,133,49,137],"svg",{"viewBox":42,"role":43,"ariaLabelledBy":44,"xmlns":47,"style":48},"0 0 720 320","img",[45,46],"ts-title","ts-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[51,52,53],"title",{"id":45},"The two in-process test clients and what they skip",[55,56,57],"desc",{"id":46},"TestClient starts a blocking portal on a second thread that runs its own event loop and drives the app, including lifespan. AsyncClient with ASGITransport calls the app on the test's own event loop and skips lifespan unless you run it yourself.",[59,60,64],"text",{"x":61,"y":62,"style":63},"360","26","text-anchor:middle;fill:currentColor;font:600 15px sans-serif","Two ways to reach the same ASGI app",[66,67],"rect",{"x":68,"y":69,"width":70,"height":71,"rx":72,"style":73},"20","46","320","196","10","fill:none;stroke:currentColor;stroke-width:1.6",[59,75,79],{"x":76,"y":77,"style":78},"180","72","text-anchor:middle;fill:currentColor;font:600 13px sans-serif","TestClient (sync test)",[66,81],{"x":82,"y":83,"width":84,"height":85,"rx":86,"style":87},"44","88","272","40","7","fill:none;stroke:currentColor;stroke-width:1.4",[59,89,92],{"x":76,"y":90,"style":91},"113","text-anchor:middle;fill:currentColor;font:12px sans-serif","test runs on the main thread",[66,94],{"x":82,"y":95,"width":84,"height":85,"rx":86,"style":87},"140",[59,97,99],{"x":76,"y":98,"style":91},"165","portal thread runs its own loop",[66,101],{"x":82,"y":102,"width":84,"height":103,"rx":86,"style":104},"192","36","fill:none;stroke:#00796B;stroke-width:1.8",[59,106,109],{"x":76,"y":107,"style":108},"215","text-anchor:middle;fill:#00796B;font:600 12px sans-serif","lifespan runs inside `with`",[66,111],{"x":112,"y":69,"width":70,"height":71,"rx":72,"style":104},"380",[59,114,117],{"x":115,"y":77,"style":116},"540","text-anchor:middle;fill:#00796B;font:600 13px sans-serif","AsyncClient + ASGITransport",[66,119],{"x":120,"y":83,"width":84,"height":85,"rx":86,"style":87},"404",[59,122,92],{"x":115,"y":90,"style":91},[66,124],{"x":120,"y":95,"width":84,"height":85,"rx":86,"style":104},[59,126,127],{"x":115,"y":98,"style":108},"app awaited on the same loop",[66,129],{"x":120,"y":102,"width":84,"height":103,"rx":86,"style":87},[59,131,132],{"x":115,"y":107,"style":91},"lifespan skipped by default",[59,134,136],{"x":61,"y":135,"style":91},"276","Neither opens a socket. Both run routing, validation and middleware in full.",[59,138,140],{"x":61,"y":139,"style":91},"298","Only the loop and the lifespan behaviour differ.",[142,143,144],"figcaption",{},"The client choice is a choice about event loops, not about syntax preference.",[146,147,149],"h2",{"id":148},"prerequisites","Prerequisites",[151,152,153,157,165],"ul",{},[154,155,156],"li",{},"FastAPI 0.139.2, httpx 0.28.1, pytest and pytest-asyncio, on Python 3.12.",[154,158,159,160,164],{},"An app whose dependencies are declared with ",[161,162,163],"code",{},"Depends",", since that is the seam every substitution below uses.",[154,166,167,168,171,172,175,176,179],{},"A ",[161,169,170],{},"pytest.ini"," (or equivalent) with ",[161,173,174],{},"asyncio_mode = auto",", otherwise pytest collects ",[161,177,178],{},"async def"," tests and refuses to run them.",[146,181,183],{"id":182},"core-mechanics-there-is-no-server","Core Mechanics: There Is No Server",[14,185,186,187,191,192,195,196,199,200,203],{},"Both supported clients drive your ASGI application ",[188,189,190],"strong",{},"in-process",". ",[161,193,194],{},"httpx.ASGITransport"," implements httpx's transport interface by building an ASGI ",[161,197,198],{},"scope",", calling ",[161,201,202],{},"app(scope, receive, send)"," and assembling the response from the messages it sends back. No socket is opened, no port is bound, nothing is serialized over TCP.",[14,205,206],{},"This is what makes these tests both fast and honest. Fast, because a request is a function call. Honest, because everything above the transport is real: your middleware stack runs, routing runs, dependencies resolve, Pydantic validates the request and serializes the response. The only fidelity you give up is at the protocol edge — HTTP\u002F1.1 parsing, connection handling, real timeouts — and that belongs in a smoke test against a deployed instance, not in your unit suite.",[14,208,209,212,213,216,217,220,221,224,225,228],{},[161,210,211],{},"TestClient"," is built on the same idea with one addition: it wraps the app in an AnyIO ",[188,214,215],{},"blocking portal",". The portal starts an event loop on a ",[18,218,219],{},"separate thread"," and gives the calling thread a synchronous handle onto it. That is how ",[161,222,223],{},"client.get(\"\u002Fx\")"," can be a plain function call with no ",[161,226,227],{},"await"," — the coroutine really does run, just not on your thread.",[14,230,231],{},"Everything in the rest of this guide follows from that one structural difference, and it is measurable:",[233,234,239],"pre",{"className":235,"code":237,"language":59,"meta":238},[236],"language-text","$ GET \u002Fpytest\u002Floops\n200 OK\n..\nsame_loop            = False\ntestclient_thread    = asyncio-portal-\u003Cid>\nasyncclient_thread   = MainThread\ncontextvar_seen_by_testclient  = 'set-by-the-test'\ncontextvar_seen_by_asyncclient = 'set-by-the-test'\n.\n3 passed in 0.00s\n","",[161,240,237],{"__ignoreMap":238},[14,242,243,244,246,247,250,251,254,255,258,259,262,263,266,267,269],{},"Those lines come from a real suite where the endpoint reports the id of the loop and the name of the thread it is running on. Under ",[161,245,211],{}," the app ran on a thread named ",[161,248,249],{},"asyncio-portal-\u003Cid>"," on a different loop from the test. Under ",[161,252,253],{},"AsyncClient"," it ran on ",[161,256,257],{},"MainThread",", on the test's own loop. Usefully, context variables set by the test were visible in ",[18,260,261],{},"both"," — AnyIO copies the caller's context into the portal — so ",[161,264,265],{},"contextvars","-based request context is not a reason to avoid ",[161,268,211],{},".",[146,271,273],{"id":272},"choosing-a-client","Choosing a Client",[233,275,279],{"className":276,"code":277,"language":278,"meta":238,"style":238},"language-python shiki shiki-themes github-light-high-contrast","from fastapi.testclient import TestClient\n\nfrom myapp import app\n\n\ndef test_health():\n    with TestClient(app) as client:\n        assert client.get(\"\u002Fhealth\").json() == {\"status\": \"ok\"}\n","python",[161,280,281,300,307,320,325,330,343,358],{"__ignoreMap":238},[282,283,286,290,294,297],"span",{"class":284,"line":285},"line",1,[282,287,289],{"class":288},"sTJeM","from",[282,291,293],{"class":292},"sigWx"," fastapi.testclient ",[282,295,296],{"class":288},"import",[282,298,299],{"class":292}," TestClient\n",[282,301,303],{"class":284,"line":302},2,[282,304,306],{"emptyLinePlaceholder":305},true,"\n",[282,308,310,312,315,317],{"class":284,"line":309},3,[282,311,289],{"class":288},[282,313,314],{"class":292}," myapp ",[282,316,296],{"class":288},[282,318,319],{"class":292}," app\n",[282,321,323],{"class":284,"line":322},4,[282,324,306],{"emptyLinePlaceholder":305},[282,326,328],{"class":284,"line":327},5,[282,329,306],{"emptyLinePlaceholder":305},[282,331,333,336,340],{"class":284,"line":332},6,[282,334,335],{"class":288},"def",[282,337,339],{"class":338},"s3dhs"," test_health",[282,341,342],{"class":292},"():\n",[282,344,346,349,352,355],{"class":284,"line":345},7,[282,347,348],{"class":288},"    with",[282,350,351],{"class":292}," TestClient(app) ",[282,353,354],{"class":288},"as",[282,356,357],{"class":292}," client:\n",[282,359,361,364,367,371,374,377,380,383,386,389],{"class":284,"line":360},8,[282,362,363],{"class":288},"        assert",[282,365,366],{"class":292}," client.get(",[282,368,370],{"class":369},"sYEJz","\"\u002Fhealth\"",[282,372,373],{"class":292},").json() ",[282,375,376],{"class":288},"==",[282,378,379],{"class":292}," {",[282,381,382],{"class":369},"\"status\"",[282,384,385],{"class":292},": ",[282,387,388],{"class":369},"\"ok\"",[282,390,391],{"class":292},"}\n",[233,393,395],{"className":276,"code":394,"language":278,"meta":238,"style":238},"from httpx import ASGITransport, AsyncClient\n\nfrom myapp import app\n\n\nasync def test_health():\n    async with AsyncClient(transport=ASGITransport(app=app), base_url=\"http:\u002F\u002Ftest\") as client:\n        assert (await client.get(\"\u002Fhealth\")).json() == {\"status\": \"ok\"}\n",[161,396,397,409,413,423,427,431,443,487],{"__ignoreMap":238},[282,398,399,401,404,406],{"class":284,"line":285},[282,400,289],{"class":288},[282,402,403],{"class":292}," httpx ",[282,405,296],{"class":288},[282,407,408],{"class":292}," ASGITransport, AsyncClient\n",[282,410,411],{"class":284,"line":302},[282,412,306],{"emptyLinePlaceholder":305},[282,414,415,417,419,421],{"class":284,"line":309},[282,416,289],{"class":288},[282,418,314],{"class":292},[282,420,296],{"class":288},[282,422,319],{"class":292},[282,424,425],{"class":284,"line":322},[282,426,306],{"emptyLinePlaceholder":305},[282,428,429],{"class":284,"line":327},[282,430,306],{"emptyLinePlaceholder":305},[282,432,433,436,439,441],{"class":284,"line":332},[282,434,435],{"class":288},"async",[282,437,438],{"class":288}," def",[282,440,339],{"class":338},[282,442,342],{"class":292},[282,444,445,448,451,454,458,461,464,467,469,472,475,477,480,483,485],{"class":284,"line":345},[282,446,447],{"class":288},"    async",[282,449,450],{"class":288}," with",[282,452,453],{"class":292}," AsyncClient(",[282,455,457],{"class":456},"sV4o_","transport",[282,459,460],{"class":288},"=",[282,462,463],{"class":292},"ASGITransport(",[282,465,466],{"class":456},"app",[282,468,460],{"class":288},[282,470,471],{"class":292},"app), ",[282,473,474],{"class":456},"base_url",[282,476,460],{"class":288},[282,478,479],{"class":369},"\"http:\u002F\u002Ftest\"",[282,481,482],{"class":292},") ",[282,484,354],{"class":288},[282,486,357],{"class":292},[282,488,489,491,494,496,498,500,503,505,507,509,511,513],{"class":284,"line":360},[282,490,363],{"class":288},[282,492,493],{"class":292}," (",[282,495,227],{"class":288},[282,497,366],{"class":292},[282,499,370],{"class":369},[282,501,502],{"class":292},")).json() ",[282,504,376],{"class":288},[282,506,379],{"class":292},[282,508,382],{"class":369},[282,510,385],{"class":292},[282,512,388],{"class":369},[282,514,391],{"class":292},[14,516,517],{},"Both are correct. The decision rule is short:",[519,520,521,537],"table",{},[522,523,524],"thead",{},[525,526,527,531,534],"tr",{},[528,529,530],"th",{},"Situation",[528,532,533],{},"Client",[528,535,536],{},"Why",[538,539,540,553,565,579,596,608],"tbody",{},[525,541,542,546,550],{},[543,544,545],"td",{},"Simple synchronous test, no async fixtures",[543,547,548],{},[161,549,211],{},[543,551,552],{},"Least ceremony; no plugin needed",[525,554,555,558,562],{},[543,556,557],{},"Test depends on an async fixture (DB session, cache, client)",[543,559,560],{},[161,561,253],{},[543,563,564],{},"Fixture and app share one event loop",[525,566,567,570,574],{},[543,568,569],{},"You need concurrent requests inside one test",[543,571,572],{},[161,573,253],{},[543,575,576,578],{},[161,577,211],{}," calls are blocking and serialise",[525,580,581,584,593],{},[543,582,583],{},"You need startup state with no extra code",[543,585,586,588,589,592],{},[161,587,211],{}," in a ",[161,590,591],{},"with"," block",[543,594,595],{},"Runs lifespan for you",[525,597,598,601,605],{},[543,599,600],{},"You are testing WebSockets",[543,602,603],{},[161,604,211],{},[543,606,607],{},"It ships a WebSocket test session",[525,609,610,613,617],{},[543,611,612],{},"The suite is already async end to end",[543,614,615],{},[161,616,253],{},[543,618,619],{},"One concurrency model throughout",[14,621,622],{},"The lifespan row is the one that surprises people, and it is worth seeing directly:",[233,624,627],{"className":625,"code":626,"language":59,"meta":238},[236],"$ GET \u002Fpytest\u002Flifespan\n200 OK\n\nTestClient inside `with`        -> lifespan_ran True\n.TestClient without `with`      -> lifespan_ran False\n.AsyncClient + ASGITransport    -> lifespan_ran False\n.AsyncClient + lifespan_context -> lifespan_ran True\n.\n4 passed in 0.00s\n",[161,628,626],{"__ignoreMap":238},[14,630,631,632,634,635,638,639,641,642,645,646,649,650,652],{},"Four real assertions against an app whose lifespan sets a flag. ",[161,633,211],{}," runs the lifespan ",[188,636,637],{},"only"," inside a ",[161,640,591],{}," block — constructing one and calling it directly silently skips startup. ",[161,643,644],{},"ASGITransport"," never runs it, because it only handles the ",[161,647,648],{},"http"," scope. When you need startup state under ",[161,651,253],{},", drive it yourself:",[233,654,656],{"className":276,"code":655,"language":278,"meta":238,"style":238},"async def test_with_startup_state():\n    async with app.router.lifespan_context(app):\n        transport = ASGITransport(app=app)\n        async with AsyncClient(transport=transport, base_url=\"http:\u002F\u002Ftest\") as client:\n            assert (await client.get(\"\u002Flifespan\")).json() == {\"lifespan_ran\": True}\n",[161,657,658,669,678,695,723],{"__ignoreMap":238},[282,659,660,662,664,667],{"class":284,"line":285},[282,661,435],{"class":288},[282,663,438],{"class":288},[282,665,666],{"class":338}," test_with_startup_state",[282,668,342],{"class":292},[282,670,671,673,675],{"class":284,"line":302},[282,672,447],{"class":288},[282,674,450],{"class":288},[282,676,677],{"class":292}," app.router.lifespan_context(app):\n",[282,679,680,683,685,688,690,692],{"class":284,"line":309},[282,681,682],{"class":292},"        transport ",[282,684,460],{"class":288},[282,686,687],{"class":292}," ASGITransport(",[282,689,466],{"class":456},[282,691,460],{"class":288},[282,693,694],{"class":292},"app)\n",[282,696,697,700,702,704,706,708,711,713,715,717,719,721],{"class":284,"line":322},[282,698,699],{"class":288},"        async",[282,701,450],{"class":288},[282,703,453],{"class":292},[282,705,457],{"class":456},[282,707,460],{"class":288},[282,709,710],{"class":292},"transport, ",[282,712,474],{"class":456},[282,714,460],{"class":288},[282,716,479],{"class":369},[282,718,482],{"class":292},[282,720,354],{"class":288},[282,722,357],{"class":292},[282,724,725,728,730,732,734,737,739,741,743,746,748,752],{"class":284,"line":327},[282,726,727],{"class":288},"            assert",[282,729,493],{"class":292},[282,731,227],{"class":288},[282,733,366],{"class":292},[282,735,736],{"class":369},"\"\u002Flifespan\"",[282,738,502],{"class":292},[282,740,376],{"class":288},[282,742,379],{"class":292},[282,744,745],{"class":369},"\"lifespan_ran\"",[282,747,385],{"class":292},[282,749,751],{"class":750},"sacAq","True",[282,753,391],{"class":292},[14,755,756,757,269],{},"The full mechanism, including what each client does to the loop your test is running on, is covered in ",[26,758,760],{"href":759},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002F","TestClient vs httpx AsyncClient",[146,762,764],{"id":763},"dependency-overrides-the-main-seam","Dependency Overrides: the Main Seam",[14,766,767,770],{},[161,768,769],{},"app.dependency_overrides"," is a dict keyed by the original dependency callable. FastAPI consults it while resolving the dependency graph, so a substitution applies to every route that declares that dependency, however deeply nested.",[14,772,773,774,777,778,781,782,785,786,788],{},"The behaviour that matters most is what happens to ",[18,775,776],{},"sub","-dependencies, and it is easy to get backwards. Consider an app where ",[161,779,780],{},"get_current_user"," depends on ",[161,783,784],{},"get_api_key",", and ",[161,787,784],{}," rejects any request without the right header:",[233,790,792],{"className":276,"code":791,"language":278,"meta":238,"style":238},"async def test_without_an_override_the_real_dependency_rejects():\n    response = await call(\"\u002Fme\")\n    assert response.status_code == 401\n\n\nasync def test_overriding_the_outer_dependency_skips_the_inner_one():\n    \"\"\"get_api_key never runs, so no header is needed at all.\"\"\"\n    app.dependency_overrides[get_current_user] = lambda: {\"id\": 99, \"name\": \"fake\", \"via\": \"override\"}\n    response = await call(\"\u002Fme\")\n    assert response.json()[\"user\"][\"id\"] == 99\n\n\nasync def test_overriding_the_inner_dependency_still_runs_the_outer():\n    \"\"\"get_api_key is faked; get_current_user still executes and wraps the fake value.\"\"\"\n    app.dependency_overrides[get_api_key] = lambda: \"test-key\"\n    response = await call(\"\u002Fme\")\n    assert response.json()[\"user\"] == {\"id\": 1, \"name\": \"real-user\", \"via\": \"test-key\"}\n",[161,793,794,805,824,837,841,845,856,861,905,920,944,949,954,966,972,987,1002],{"__ignoreMap":238},[282,795,796,798,800,803],{"class":284,"line":285},[282,797,435],{"class":288},[282,799,438],{"class":288},[282,801,802],{"class":338}," test_without_an_override_the_real_dependency_rejects",[282,804,342],{"class":292},[282,806,807,810,812,815,818,821],{"class":284,"line":302},[282,808,809],{"class":292},"    response ",[282,811,460],{"class":288},[282,813,814],{"class":288}," await",[282,816,817],{"class":292}," call(",[282,819,820],{"class":369},"\"\u002Fme\"",[282,822,823],{"class":292},")\n",[282,825,826,829,832,834],{"class":284,"line":309},[282,827,828],{"class":288},"    assert",[282,830,831],{"class":292}," response.status_code ",[282,833,376],{"class":288},[282,835,836],{"class":750}," 401\n",[282,838,839],{"class":284,"line":322},[282,840,306],{"emptyLinePlaceholder":305},[282,842,843],{"class":284,"line":327},[282,844,306],{"emptyLinePlaceholder":305},[282,846,847,849,851,854],{"class":284,"line":332},[282,848,435],{"class":288},[282,850,438],{"class":288},[282,852,853],{"class":338}," test_overriding_the_outer_dependency_skips_the_inner_one",[282,855,342],{"class":292},[282,857,858],{"class":284,"line":345},[282,859,860],{"class":369},"    \"\"\"get_api_key never runs, so no header is needed at all.\"\"\"\n",[282,862,863,866,868,871,874,877,879,882,885,888,890,893,895,898,900,903],{"class":284,"line":360},[282,864,865],{"class":292},"    app.dependency_overrides[get_current_user] ",[282,867,460],{"class":288},[282,869,870],{"class":288}," lambda",[282,872,873],{"class":292},": {",[282,875,876],{"class":369},"\"id\"",[282,878,385],{"class":292},[282,880,881],{"class":750},"99",[282,883,884],{"class":292},", ",[282,886,887],{"class":369},"\"name\"",[282,889,385],{"class":292},[282,891,892],{"class":369},"\"fake\"",[282,894,884],{"class":292},[282,896,897],{"class":369},"\"via\"",[282,899,385],{"class":292},[282,901,902],{"class":369},"\"override\"",[282,904,391],{"class":292},[282,906,908,910,912,914,916,918],{"class":284,"line":907},9,[282,909,809],{"class":292},[282,911,460],{"class":288},[282,913,814],{"class":288},[282,915,817],{"class":292},[282,917,820],{"class":369},[282,919,823],{"class":292},[282,921,923,925,928,931,934,936,939,941],{"class":284,"line":922},10,[282,924,828],{"class":288},[282,926,927],{"class":292}," response.json()[",[282,929,930],{"class":369},"\"user\"",[282,932,933],{"class":292},"][",[282,935,876],{"class":369},[282,937,938],{"class":292},"] ",[282,940,376],{"class":288},[282,942,943],{"class":750}," 99\n",[282,945,947],{"class":284,"line":946},11,[282,948,306],{"emptyLinePlaceholder":305},[282,950,952],{"class":284,"line":951},12,[282,953,306],{"emptyLinePlaceholder":305},[282,955,957,959,961,964],{"class":284,"line":956},13,[282,958,435],{"class":288},[282,960,438],{"class":288},[282,962,963],{"class":338}," test_overriding_the_inner_dependency_still_runs_the_outer",[282,965,342],{"class":292},[282,967,969],{"class":284,"line":968},14,[282,970,971],{"class":369},"    \"\"\"get_api_key is faked; get_current_user still executes and wraps the fake value.\"\"\"\n",[282,973,975,978,980,982,984],{"class":284,"line":974},15,[282,976,977],{"class":292},"    app.dependency_overrides[get_api_key] ",[282,979,460],{"class":288},[282,981,870],{"class":288},[282,983,385],{"class":292},[282,985,986],{"class":369},"\"test-key\"\n",[282,988,990,992,994,996,998,1000],{"class":284,"line":989},16,[282,991,809],{"class":292},[282,993,460],{"class":288},[282,995,814],{"class":288},[282,997,817],{"class":292},[282,999,820],{"class":369},[282,1001,823],{"class":292},[282,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1024,1026,1028,1030,1033,1035,1037,1039,1042],{"class":284,"line":1004},17,[282,1006,828],{"class":288},[282,1008,927],{"class":292},[282,1010,930],{"class":369},[282,1012,938],{"class":292},[282,1014,376],{"class":288},[282,1016,379],{"class":292},[282,1018,876],{"class":369},[282,1020,385],{"class":292},[282,1022,1023],{"class":750},"1",[282,1025,884],{"class":292},[282,1027,887],{"class":369},[282,1029,385],{"class":292},[282,1031,1032],{"class":369},"\"real-user\"",[282,1034,884],{"class":292},[282,1036,897],{"class":369},[282,1038,385],{"class":292},[282,1040,1041],{"class":369},"\"test-key\"",[282,1043,391],{"class":292},[14,1045,1046],{},"Real output from that suite:",[233,1048,1051],{"className":1049,"code":1050,"language":59,"meta":238},[236],"$ GET \u002Fpytest\u002Foverrides\n200 OK\n\nno header, no override   -> 401 {'detail': 'bad api key'}\n.real header              -> 200 {'user': {'id': 1, 'name': 'real-user', 'via': 'live-secret'}}\n.override get_current_user-> 200 {'user': {'id': 99, 'name': 'fake', 'via': 'override'}}\n.override get_api_key     -> 200 {'user': {'id': 1, 'name': 'real-user', 'via': 'test-key'}}\n.after clear()            -> 401 {'detail': 'bad api key'}\n.\n5 passed in 0.00s\n",[161,1052,1050],{"__ignoreMap":238},[14,1054,1055,1056,1058,1059,1062,1063,1065,1066,1068,1069,1071,1072,1075,1076,1079,1080,1082],{},"Read the third and fourth lines together. Overriding ",[161,1057,780],{}," produced ",[161,1060,1061],{},"id: 99"," — the real ",[161,1064,780],{}," never ran, so neither did ",[161,1067,784],{},", and the header requirement disappeared entirely. Overriding ",[161,1070,784],{}," instead produced ",[161,1073,1074],{},"id: 1"," with ",[161,1077,1078],{},"via: 'test-key'",": the real ",[161,1081,780],{}," still executed, and simply received a fake key. Both are useful; they answer different questions. Override the outer dependency to bypass authentication wholesale; override the inner one when the code under test is the outer dependency itself.",[14,1084,1085],{},"The last line is the discipline. Overrides live on the app object, which is a module-level singleton shared by every test in the process. Always clear them in teardown:",[233,1087,1089],{"className":276,"code":1088,"language":278,"meta":238,"style":238},"import pytest\n\n\n@pytest.fixture(autouse=True)\ndef clean_overrides():\n    yield\n    app.dependency_overrides.clear()\n",[161,1090,1091,1098,1102,1106,1123,1132,1137],{"__ignoreMap":238},[282,1092,1093,1095],{"class":284,"line":285},[282,1094,296],{"class":288},[282,1096,1097],{"class":292}," pytest\n",[282,1099,1100],{"class":284,"line":302},[282,1101,306],{"emptyLinePlaceholder":305},[282,1103,1104],{"class":284,"line":309},[282,1105,306],{"emptyLinePlaceholder":305},[282,1107,1108,1111,1114,1117,1119,1121],{"class":284,"line":322},[282,1109,1110],{"class":338},"@pytest.fixture",[282,1112,1113],{"class":292},"(",[282,1115,1116],{"class":456},"autouse",[282,1118,460],{"class":288},[282,1120,751],{"class":750},[282,1122,823],{"class":292},[282,1124,1125,1127,1130],{"class":284,"line":327},[282,1126,335],{"class":288},[282,1128,1129],{"class":338}," clean_overrides",[282,1131,342],{"class":292},[282,1133,1134],{"class":284,"line":332},[282,1135,1136],{"class":288},"    yield\n",[282,1138,1139],{"class":284,"line":345},[282,1140,1141],{"class":292},"    app.dependency_overrides.clear()\n",[14,1143,1144,1147,1148,1151,1152,269],{},[161,1145,1146],{},"autouse=True"," is deliberate here — the one test that forgets to request the fixture is the one that poisons the suite. The broader substitution patterns, including overriding ",[161,1149,1150],{},"yield"," dependencies and settings objects, are covered in ",[26,1153,1155],{"href":1154},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","Overriding Dependencies in Tests",[146,1157,1159],{"id":1158},"fixtures-build-them-in-layers","Fixtures: Build Them in Layers",[14,1161,1162],{},"A good FastAPI suite has a short, boring fixture chain where each fixture does exactly one thing:",[1164,1165,1166,1172,1178],"ol",{},[154,1167,1168,1171],{},[188,1169,1170],{},"Resource fixtures"," (session-scoped): an engine, a fake message broker, a stubbed HTTP layer. Expensive, built once.",[154,1173,1174,1177],{},[188,1175,1176],{},"Isolation fixtures"," (function-scoped): a transaction that will be rolled back, a cleared cache.",[154,1179,1180,1183,1184,1187],{},[188,1181,1182],{},"Wiring fixtures"," (function-scoped): install ",[161,1185,1186],{},"dependency_overrides",", hand back a client, clean up.",[14,1189,1190,1191,269],{},"The database instance of that chain — a session-scoped engine, a per-test transaction, and a client bound to it — is worked through in full, with a passing transcript, in ",[26,1192,1194],{"href":1193},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002F","Testing with Async Database Fixtures",[14,1196,1197,1198,1201],{},"The rule that keeps async fixtures working is about lifetimes: ",[188,1199,1200],{},"the event loop must live at least as long as the longest-lived async fixture."," A session-scoped async fixture on a function-scoped loop is the classic cause of \"attached to a different loop\" errors, and the fix is configuration rather than code:",[233,1203,1207],{"className":1204,"code":1205,"language":1206,"meta":238,"style":238},"language-ini shiki shiki-themes github-light-high-contrast","[pytest]\nasyncio_mode = auto\nasyncio_default_fixture_loop_scope = session\n","ini",[161,1208,1209,1214,1219],{"__ignoreMap":238},[282,1210,1211],{"class":284,"line":285},[282,1212,1213],{},"[pytest]\n",[282,1215,1216],{"class":284,"line":302},[282,1217,1218],{},"asyncio_mode = auto\n",[282,1220,1221],{"class":284,"line":309},[282,1222,1223],{},"asyncio_default_fixture_loop_scope = session\n",[14,1225,1226,1227,1230],{},"Fixtures also want to be ",[18,1228,1229],{},"values",", not factories, wherever possible. A fixture that returns an already-open session lets the test assert against the same object the endpoint used, which is what makes \"did the endpoint really write this row?\" a one-line assertion.",[146,1232,1234],{"id":1233},"testing-background-tasks","Testing Background Tasks",[14,1236,1237,1240,1241,1244],{},[161,1238,1239],{},"BackgroundTasks"," are executed by Starlette as part of completing the response — the ASGI call does not finish until the tasks have run. Because both test clients await that same ASGI call, tasks have ",[188,1242,1243],{},"already completed"," when you get the response object back. No sleeping, no polling.",[233,1246,1248],{"className":276,"code":1247,"language":278,"meta":238,"style":238},"def test_background_task_has_already_run_when_testclient_returns():\n    AUDIT.clear()\n    with TestClient(app) as client:\n        response = client.post(\"\u002Forders\", params={\"sku\": \"A-1\"})\n        assert response.json() == {\"sku\": \"A-1\", \"queued\": True}\n    assert AUDIT == [\"order:A-1:by:7\"]\n\n\nasync def test_background_task_also_completes_under_asgitransport():\n    AUDIT.clear()\n    async with AsyncClient(transport=ASGITransport(app=app), base_url=\"http:\u002F\u002Ft\") as client:\n        await client.post(\"\u002Forders\", params={\"sku\": \"B-2\"})\n    assert AUDIT == [\"order:B-2:by:7\"]\n",[161,1249,1250,1259,1267,1277,1311,1339,1358,1362,1366,1377,1383,1416,1442],{"__ignoreMap":238},[282,1251,1252,1254,1257],{"class":284,"line":285},[282,1253,335],{"class":288},[282,1255,1256],{"class":338}," test_background_task_has_already_run_when_testclient_returns",[282,1258,342],{"class":292},[282,1260,1261,1264],{"class":284,"line":302},[282,1262,1263],{"class":750},"    AUDIT",[282,1265,1266],{"class":292},".clear()\n",[282,1268,1269,1271,1273,1275],{"class":284,"line":309},[282,1270,348],{"class":288},[282,1272,351],{"class":292},[282,1274,354],{"class":288},[282,1276,357],{"class":292},[282,1278,1279,1282,1284,1287,1290,1292,1295,1297,1300,1303,1305,1308],{"class":284,"line":322},[282,1280,1281],{"class":292},"        response ",[282,1283,460],{"class":288},[282,1285,1286],{"class":292}," client.post(",[282,1288,1289],{"class":369},"\"\u002Forders\"",[282,1291,884],{"class":292},[282,1293,1294],{"class":456},"params",[282,1296,460],{"class":288},[282,1298,1299],{"class":292},"{",[282,1301,1302],{"class":369},"\"sku\"",[282,1304,385],{"class":292},[282,1306,1307],{"class":369},"\"A-1\"",[282,1309,1310],{"class":292},"})\n",[282,1312,1313,1315,1318,1320,1322,1324,1326,1328,1330,1333,1335,1337],{"class":284,"line":327},[282,1314,363],{"class":288},[282,1316,1317],{"class":292}," response.json() ",[282,1319,376],{"class":288},[282,1321,379],{"class":292},[282,1323,1302],{"class":369},[282,1325,385],{"class":292},[282,1327,1307],{"class":369},[282,1329,884],{"class":292},[282,1331,1332],{"class":369},"\"queued\"",[282,1334,385],{"class":292},[282,1336,751],{"class":750},[282,1338,391],{"class":292},[282,1340,1341,1343,1346,1349,1352,1355],{"class":284,"line":332},[282,1342,828],{"class":288},[282,1344,1345],{"class":750}," AUDIT",[282,1347,1348],{"class":288}," ==",[282,1350,1351],{"class":292}," [",[282,1353,1354],{"class":369},"\"order:A-1:by:7\"",[282,1356,1357],{"class":292},"]\n",[282,1359,1360],{"class":284,"line":345},[282,1361,306],{"emptyLinePlaceholder":305},[282,1363,1364],{"class":284,"line":360},[282,1365,306],{"emptyLinePlaceholder":305},[282,1367,1368,1370,1372,1375],{"class":284,"line":907},[282,1369,435],{"class":288},[282,1371,438],{"class":288},[282,1373,1374],{"class":338}," test_background_task_also_completes_under_asgitransport",[282,1376,342],{"class":292},[282,1378,1379,1381],{"class":284,"line":922},[282,1380,1263],{"class":750},[282,1382,1266],{"class":292},[282,1384,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405,1407,1410,1412,1414],{"class":284,"line":946},[282,1386,447],{"class":288},[282,1388,450],{"class":288},[282,1390,453],{"class":292},[282,1392,457],{"class":456},[282,1394,460],{"class":288},[282,1396,463],{"class":292},[282,1398,466],{"class":456},[282,1400,460],{"class":288},[282,1402,471],{"class":292},[282,1404,474],{"class":456},[282,1406,460],{"class":288},[282,1408,1409],{"class":369},"\"http:\u002F\u002Ft\"",[282,1411,482],{"class":292},[282,1413,354],{"class":288},[282,1415,357],{"class":292},[282,1417,1418,1421,1423,1425,1427,1429,1431,1433,1435,1437,1440],{"class":284,"line":951},[282,1419,1420],{"class":288},"        await",[282,1422,1286],{"class":292},[282,1424,1289],{"class":369},[282,1426,884],{"class":292},[282,1428,1294],{"class":456},[282,1430,460],{"class":288},[282,1432,1299],{"class":292},[282,1434,1302],{"class":369},[282,1436,385],{"class":292},[282,1438,1439],{"class":369},"\"B-2\"",[282,1441,1310],{"class":292},[282,1443,1444,1446,1448,1450,1452,1455],{"class":284,"line":956},[282,1445,828],{"class":288},[282,1447,1345],{"class":750},[282,1449,1348],{"class":288},[282,1451,1351],{"class":292},[282,1453,1454],{"class":369},"\"order:B-2:by:7\"",[282,1456,1357],{"class":292},[233,1458,1461],{"className":1459,"code":1460,"language":59,"meta":238},[236],"$ GET \u002Fpytest\u002Fbackground\n200 OK\n\nTestClient: audit right after the response  = ['order:A-1:by:7']\n.AsyncClient: audit right after the response = ['order:B-2:by:7']\n.\n2 passed in 0.00s\n",[161,1462,1460],{"__ignoreMap":238},[14,1464,1465,1466,1469],{},"Both clients behave identically here, and both printed the side effect ",[18,1467,1468],{},"immediately"," after receiving the response. That determinism is a gift: assert on the observable effect (a row written, a message enqueued, a counter incremented) rather than on the task function having been called.",[14,1471,1472,1473,1475,1476,1480,1481,269],{},"Two caveats. This is ",[161,1474,1239],{},", not a real queue — the behaviour of an external worker cannot be asserted this way, and the differences are laid out in ",[26,1477,1479],{"href":1478},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002F","FastAPI BackgroundTasks vs Celery vs arq",". And a task that raises does not fail the response, so a test asserting only on status code will not notice — see ",[26,1482,1484],{"href":1483},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002F","When BackgroundTasks Silently Fails",[146,1486,1488],{"id":1487},"unit-versus-integration","Unit Versus Integration",[14,1490,1491,1492],{},"The distinction that matters in a FastAPI codebase is not \"fast versus slow\" but ",[188,1493,1494],{},"\"does the framework participate?\"",[14,1496,167,1497,1500],{},[188,1498,1499],{},"unit test"," covers something you could extract into a library: a pricing function, a Pydantic model's validators, a retry policy. No app, no client, no overrides. These should be the majority of your suite and should run in milliseconds.",[14,1502,1503,1504,1507],{},"An ",[188,1505,1506],{},"integration test"," goes through the app with one of the two clients. It is the only way to test routing, parameter binding, validation, response models, middleware ordering and dependency resolution — which is to say, most of what FastAPI is.",[14,1509,1510],{},"There is a tempting third option that is worse than either: importing the path operation function and calling it directly. It looks like a unit test and tests almost nothing.",[233,1512,1515],{"className":1513,"code":1514,"language":59,"meta":238},[236],"$ GET \u002Fpytest\u002Flayers\n200 OK\n\ncreate_order parameters: ['user', 'tasks', 'sku']\nannotation of `user`   : typing.Annotated[dict, Depends(dependency=\u003Cfunction get_current_user at 0xADDR>, use_cache=True, scope=None)]\nannotation of `tasks`  : BackgroundTasks\n-> calling create_order(...) yourself means constructing all three by hand\n.missing required query param -> 422\n{'detail': [{'type': 'missing', 'loc': ['query', 'sku'], 'msg': 'Field required', 'input': None}]}\n.\n2 passed in 0.00s\n",[161,1516,1514],{"__ignoreMap":238},[14,1518,1519,1520,1523,1524,1527,1528,1530,1531,1534,1535,1537,1538,1541,1542,1545,1546,1549],{},"The first half is introspection of a real endpoint: to call ",[161,1521,1522],{},"create_order"," yourself you would have to fabricate a ",[161,1525,1526],{},"user"," dict, construct a ",[161,1529,1239],{}," instance, and supply ",[161,1532,1533],{},"sku"," directly — which means dependency resolution, background-task collection and query parameter parsing are all bypassed. The second half shows what you lose: going through the app, a missing ",[161,1536,1533],{}," produced a real 422 with a ",[161,1539,1540],{},"loc"," of ",[161,1543,1544],{},"['query', 'sku']",". Called directly, that request would have been a ",[161,1547,1548],{},"TypeError"," in your test, or worse, would have \"passed\" with a hand-made value that no real client could ever send.",[14,1551,1552],{},"A workable split for a typical service:",[519,1554,1555,1570],{},[522,1556,1557],{},[525,1558,1559,1562,1564,1567],{},[528,1560,1561],{},"Layer",[528,1563,533],{},[528,1565,1566],{},"What it proves",[528,1568,1569],{},"Rough share",[538,1571,1572,1586,1605,1621],{},[525,1573,1574,1577,1580,1583],{},[543,1575,1576],{},"Domain functions and models",[543,1578,1579],{},"none",[543,1581,1582],{},"Business rules, validation logic",[543,1584,1585],{},"60%",[525,1587,1588,1591,1599,1602],{},[543,1589,1590],{},"Endpoint behaviour",[543,1592,1593,1595,1596,1598],{},[161,1594,253],{}," or ",[161,1597,211],{}," with overrides",[543,1600,1601],{},"Routing, DI, validation, status codes",[543,1603,1604],{},"30%",[525,1606,1607,1610,1615,1618],{},[543,1608,1609],{},"Database-backed flows",[543,1611,1612,1614],{},[161,1613,253],{}," + transaction fixture",[543,1616,1617],{},"Real SQL, constraints, boundaries",[543,1619,1620],{},"8%",[525,1622,1623,1626,1629,1632],{},[543,1624,1625],{},"Deployed smoke tests",[543,1627,1628],{},"real HTTP",[543,1630,1631],{},"TLS, proxy, config, migrations",[543,1633,1634],{},"2%",[146,1636,1638],{"id":1637},"what-to-assert-on-a-response","What to Assert on a Response",[14,1640,1641,1642,1645],{},"A large share of low-value FastAPI tests assert ",[161,1643,1644],{},"response.status_code == 200"," and stop. That check passes for a handler returning the wrong user's data, so it is worth being deliberate about what an endpoint test is actually for.",[14,1647,1648,1649,1652],{},"Assert the ",[188,1650,1651],{},"status code and the body shape together",". The status is the contract with HTTP; the body is the contract with the client. A response model change that silently drops a field will not move the status code.",[233,1654,1656],{"className":276,"code":1655,"language":278,"meta":238,"style":238},"async def test_create_returns_the_created_resource(client):\n    response = await client.post(\"\u002Fwidgets\", params={\"name\": \"sprocket\"})\n    assert response.status_code == 200\n    assert response.json() == {\"id\": 1, \"name\": \"sprocket\"}\n",[161,1657,1658,1670,1700,1711],{"__ignoreMap":238},[282,1659,1660,1662,1664,1667],{"class":284,"line":285},[282,1661,435],{"class":288},[282,1663,438],{"class":288},[282,1665,1666],{"class":338}," test_create_returns_the_created_resource",[282,1668,1669],{"class":292},"(client):\n",[282,1671,1672,1674,1676,1678,1680,1683,1685,1687,1689,1691,1693,1695,1698],{"class":284,"line":302},[282,1673,809],{"class":292},[282,1675,460],{"class":288},[282,1677,814],{"class":288},[282,1679,1286],{"class":292},[282,1681,1682],{"class":369},"\"\u002Fwidgets\"",[282,1684,884],{"class":292},[282,1686,1294],{"class":456},[282,1688,460],{"class":288},[282,1690,1299],{"class":292},[282,1692,887],{"class":369},[282,1694,385],{"class":292},[282,1696,1697],{"class":369},"\"sprocket\"",[282,1699,1310],{"class":292},[282,1701,1702,1704,1706,1708],{"class":284,"line":309},[282,1703,828],{"class":288},[282,1705,831],{"class":292},[282,1707,376],{"class":288},[282,1709,1710],{"class":750}," 200\n",[282,1712,1713,1715,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735],{"class":284,"line":322},[282,1714,828],{"class":288},[282,1716,1317],{"class":292},[282,1718,376],{"class":288},[282,1720,379],{"class":292},[282,1722,876],{"class":369},[282,1724,385],{"class":292},[282,1726,1023],{"class":750},[282,1728,884],{"class":292},[282,1730,887],{"class":369},[282,1732,385],{"class":292},[282,1734,1697],{"class":369},[282,1736,391],{"class":292},[14,1738,1739,1740,1743,1744,1747],{},"Prefer whole-object equality over field-by-field checks where the payload is small. ",[161,1741,1742],{},"assert response.json() == {...}"," fails when a field is ",[18,1745,1746],{},"added"," as well as when one is removed, which is exactly the sensitivity you want from a serialization test.",[14,1749,1648,1750,1755,1756,1758],{},[188,1751,1752,1754],{},[161,1753,1540],{}," of validation errors",", not just the 422. FastAPI's error bodies are structured, and the ",[161,1757,1540],{}," tuple names the parameter and where it came from. Asserting on it means the test breaks when a parameter moves from query to body — a real API-breaking change that a bare status check would miss:",[233,1760,1762],{"className":276,"code":1761,"language":278,"meta":238,"style":238},"async def test_missing_sku_is_reported_against_the_query_string(client):\n    body = (await client.post(\"\u002Forders\")).json()\n    assert body[\"detail\"][0][\"loc\"] == [\"query\", \"sku\"]\n    assert body[\"detail\"][0][\"type\"] == \"missing\"\n",[161,1763,1764,1775,1793,1828],{"__ignoreMap":238},[282,1765,1766,1768,1770,1773],{"class":284,"line":285},[282,1767,435],{"class":288},[282,1769,438],{"class":288},[282,1771,1772],{"class":338}," test_missing_sku_is_reported_against_the_query_string",[282,1774,1669],{"class":292},[282,1776,1777,1780,1782,1784,1786,1788,1790],{"class":284,"line":302},[282,1778,1779],{"class":292},"    body ",[282,1781,460],{"class":288},[282,1783,493],{"class":292},[282,1785,227],{"class":288},[282,1787,1286],{"class":292},[282,1789,1289],{"class":369},[282,1791,1792],{"class":292},")).json()\n",[282,1794,1795,1797,1800,1803,1805,1808,1810,1813,1815,1817,1819,1822,1824,1826],{"class":284,"line":309},[282,1796,828],{"class":288},[282,1798,1799],{"class":292}," body[",[282,1801,1802],{"class":369},"\"detail\"",[282,1804,933],{"class":292},[282,1806,1807],{"class":750},"0",[282,1809,933],{"class":292},[282,1811,1812],{"class":369},"\"loc\"",[282,1814,938],{"class":292},[282,1816,376],{"class":288},[282,1818,1351],{"class":292},[282,1820,1821],{"class":369},"\"query\"",[282,1823,884],{"class":292},[282,1825,1302],{"class":369},[282,1827,1357],{"class":292},[282,1829,1830,1832,1834,1836,1838,1840,1842,1845,1847,1849],{"class":284,"line":322},[282,1831,828],{"class":288},[282,1833,1799],{"class":292},[282,1835,1802],{"class":369},[282,1837,933],{"class":292},[282,1839,1807],{"class":750},[282,1841,933],{"class":292},[282,1843,1844],{"class":369},"\"type\"",[282,1846,938],{"class":292},[282,1848,376],{"class":288},[282,1850,1851],{"class":369}," \"missing\"\n",[14,1853,1854,1855,1858],{},"Assert ",[188,1856,1857],{},"side effects at their observable boundary",". If an endpoint writes a row, query the row. If it enqueues a message, inspect the queue. Asserting that a mock was called with certain arguments couples the test to the implementation and keeps passing after a refactor that breaks the behaviour.",[14,1860,1861,1862,1865,1866,1869],{},"And assert the ",[188,1863,1864],{},"failure paths deliberately",". The 401, the 403, the 409 on a conflicting write, the 422 on bad input: these are the responses your clients handle in production and the ones least likely to be exercised by hand. A useful heuristic is that every ",[161,1867,1868],{},"raise HTTPException"," in the codebase should be named by at least one test.",[146,1871,1873],{"id":1872},"async-and-performance-notes","Async and Performance Notes",[14,1875,1876],{},"In-process tests are fast enough that suite time is usually dominated by fixtures rather than requests. Two habits keep it that way.",[14,1878,1879],{},"Make expensive things session-scoped and cheap things function-scoped. An engine, a loaded model, a compiled schema: build once. A transaction, an override, a cleared counter: rebuild per test.",[14,1881,1882,1883,1886,1887,1890,1891,1895,1896,1900],{},"Do not put ",[161,1884,1885],{},"time.sleep"," in a test. If you are waiting for something, either the thing is synchronous and you do not need to wait, or it is asynchronous and you should await it. The one legitimate use of wall-clock in a test is ",[18,1888,1889],{},"asserting"," on it — the timing assertions in ",[26,1892,1894],{"href":1893},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002F","Running Sync Code in a Threadpool"," and ",[26,1897,1899],{"href":1898},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002F","Concurrent Requests with asyncio.gather"," are the pattern, and they are the only reliable way to catch a blocking call in review.",[14,1902,1903,1904,191,1906,1908,1909,1912,1913,1915],{},"Concurrency inside a single test needs ",[161,1905,253],{},[161,1907,211],{}," calls block the calling thread, so a loop of three requests really does take the sum of their durations, while ",[161,1910,1911],{},"asyncio.gather"," over an ",[161,1914,253],{}," takes the maximum.",[146,1917,1919],{"id":1918},"failure-modes-and-diagnosis","Failure Modes and Diagnosis",[14,1921,1922,1927,1928,269],{},[188,1923,1924],{},[161,1925,1926],{},"async def functions are not natively supported"," — pytest collected an async test with no async plugin active. Install pytest-asyncio and set ",[161,1929,174],{},[14,1931,1932,1943,1944,1946,1947,1949,1950,1952,1953,269],{},[188,1933,1934,1935,1938,1939,1942],{},"Startup state is missing (",[161,1936,1937],{},"AttributeError"," on ",[161,1940,1941],{},"app.state.x",")"," — you used ",[161,1945,644],{}," without running the lifespan, or built a ",[161,1948,211],{}," outside a ",[161,1951,591],{}," block. Wrap it, or enter ",[161,1954,1955],{},"app.router.lifespan_context(app)",[14,1957,1958,1961,1962,1965],{},[188,1959,1960],{},"\"attached to a different loop\""," — an async fixture and its consumer are on different event loops. Align ",[161,1963,1964],{},"asyncio_default_fixture_loop_scope"," with the fixture's scope.",[14,1967,1968,1971,1972,1974,1975,1977],{},[188,1969,1970],{},"Tests pass alone and fail together"," — an override or module-level state leaked. Add an ",[161,1973,1116],{}," fixture that clears ",[161,1976,1186],{},", and check for caches or singletons built at import time.",[14,1979,1980,1983],{},[188,1981,1982],{},"Tests pass but the database is untouched"," — the session override is not installed, so the app is quietly using its real engine. Assert that the test's own session can see the row the endpoint wrote.",[14,1985,1986,1989,1990,1992],{},[188,1987,1988],{},"A 422 you did not expect"," — good news, actually: the request your test sends is not the request the endpoint declares. Read the ",[161,1991,1540],{}," field; it names the exact parameter and its source.",[14,1994,1995,1998],{},[188,1996,1997],{},"Everything passes and production breaks on the first request"," — your tests never ran the lifespan, so a misconfigured startup path was never exercised. Keep at least one test that enters the real lifespan.",[146,2000,2002],{"id":2001},"faq","FAQ",[14,2004,2005,2008,2009,2011,2012,1075,2015,2017,2018,2020],{},[188,2006,2007],{},"Should I use TestClient or httpx.AsyncClient?","\nUse ",[161,2010,211],{}," for synchronous tests where you want the simplest possible call, and ",[161,2013,2014],{},"httpx.AsyncClient",[161,2016,644],{}," whenever the test is async or depends on async fixtures. ",[161,2019,253],{}," runs the app on the loop the test is already using, which is what lets fixtures and application code share loop-bound resources.",[14,2022,2023,2026],{},[188,2024,2025],{},"What does ASGITransport actually do?","\nIt is an httpx transport that calls your ASGI application directly in-process instead of opening a socket. There is no server, no port and no network, so requests are fast and deterministic while still going through the full middleware, routing, validation and serialization stack.",[14,2028,2029,2032,2033,2035,2036,2038],{},[188,2030,2031],{},"Does ASGITransport run startup and shutdown events?","\nNo. ",[161,2034,644],{}," only handles the ",[161,2037,648],{}," scope, so lifespan never runs and anything created at startup will be missing. Drive the lifespan yourself with the app's lifespan context, or use a lifespan manager helper.",[14,2040,2041,2044,2045,2047],{},[188,2042,2043],{},"How do I replace a dependency in a test?","\nAssign a replacement callable to ",[161,2046,769],{}," keyed by the original dependency function, then clear the overrides in fixture teardown. Overriding an outer dependency skips its sub-dependencies entirely, which is usually what you want for authentication.",[14,2049,2050,2053,2054,2056],{},[188,2051,2052],{},"Have background tasks finished when the test client returns?","\nYes. ",[161,2055,1239],{}," are executed by Starlette as part of completing the response, so by the time the client hands you a response object the tasks have already run and you can assert on their side effects directly.",[14,2058,2059,2062],{},[188,2060,2061],{},"What belongs in a unit test versus an integration test?","\nUnit tests cover plain functions and Pydantic models with no client at all. Integration tests go through the app so that routing, dependency resolution, validation and serialization are exercised. Calling a path operation function directly is neither, because it bypasses everything FastAPI does for you.",[146,2064,2066],{"id":2065},"related","Related",[151,2068,2069,2077,2085,2095,2105,2113],{},[154,2070,2071,2074,2075,269],{},[188,2072,2073],{},"Up to the section:"," ",[26,2076,29],{"href":28},[154,2078,2079,2074,2082,2084],{},[188,2080,2081],{},"The client decision in depth:",[26,2083,760],{"href":759}," measures what each one does to your event loop.",[154,2086,2087,2074,2090,2094],{},[188,2088,2089],{},"Async test mechanics:",[26,2091,2093],{"href":2092},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002F","Testing Async Endpoints with pytest-asyncio"," covers markers, modes and loop scopes.",[154,2096,2097,2074,2100,2104],{},[188,2098,2099],{},"Isolating from the outside world:",[26,2101,2103],{"href":2102},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002F","Mocking External Services in Tests"," replaces upstream HTTP without weakening the test.",[154,2106,2107,2074,2110,2112],{},[188,2108,2109],{},"Database fixtures:",[26,2111,1194],{"href":1193}," builds the rollback-per-test chain end to end.",[154,2114,2115,2074,2118,269],{},[188,2116,2117],{},"The substitution mechanism:",[26,2119,1155],{"href":1154},[2121,2122,2123],"style",{},"html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}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 .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}",{"title":238,"searchDepth":302,"depth":302,"links":2125},[2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137],{"id":148,"depth":302,"text":149},{"id":182,"depth":302,"text":183},{"id":272,"depth":302,"text":273},{"id":763,"depth":302,"text":764},{"id":1158,"depth":302,"text":1159},{"id":1233,"depth":302,"text":1234},{"id":1487,"depth":302,"text":1488},{"id":1637,"depth":302,"text":1638},{"id":1872,"depth":302,"text":1873},{"id":1918,"depth":302,"text":1919},{"id":2001,"depth":302,"text":2002},{"id":2065,"depth":302,"text":2066},"2026-07-20","A testing strategy for FastAPI: choosing TestClient or httpx AsyncClient, ASGITransport, dependency overrides, async fixtures, and unit versus integration.","md",[2142,2144,2146,2148,2150,2152],{"q":2007,"a":2143},"Use TestClient for synchronous tests where you want the simplest possible call, and httpx.AsyncClient with ASGITransport whenever the test is async or depends on async fixtures. AsyncClient runs the app on the loop the test is already using, which is what lets fixtures and application code share loop-bound resources.",{"q":2025,"a":2145},"It is an httpx transport that calls your ASGI application directly in-process instead of opening a socket. There is no server, no port and no network, so requests are fast and deterministic while still going through the full middleware, routing, validation and serialization stack.",{"q":2031,"a":2147},"No. ASGITransport only handles the http scope, so lifespan never runs and anything created at startup will be missing. Drive the lifespan yourself with the app's lifespan context, or use a lifespan manager helper.",{"q":2043,"a":2149},"Assign a replacement callable to app.dependency_overrides keyed by the original dependency function, then clear the overrides in fixture teardown. Overriding an outer dependency skips its sub-dependencies entirely, which is usually what you want for authentication.",{"q":2052,"a":2151},"Yes. BackgroundTasks are executed by Starlette as part of completing the response, so by the time the client hands you a response object the tasks have already run and you can assert on their side effects directly.",{"q":2061,"a":2153},"Unit tests cover plain functions and Pydantic models with no client at all. Integration tests go through the app so that routing, dependency resolution, validation and serialization are exercised. Calling a path operation function directly is neither, because it bypasses everything FastAPI does for you.",null,{"slug":12,"breadcrumb":2156},[2157,2160,2162],{"label":2158,"path":2159},"Home","\u002F",{"label":2161,"path":28},"Async, Background Tasks & Observability",{"label":5,"path":2163},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002F","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications",{"title":5,"description":2139},"async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex","topic","p7z_TdoG4NcGCDssPTBuh6O9rlk3Jgt9l_XRVHjw3us",[2170,2352,2549],{"title":2171,"path":2172,"stem":2173,"children":2174,"page":-1},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[2175,2178,2208,2232,2256,2280,2316,2340],{"title":2176,"path":2172,"stem":2177},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":2179,"path":2180,"stem":2181,"children":2182,"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",[2183,2184,2190,2196,2202],{"title":2179,"path":2180,"stem":2181},{"title":2185,"path":2186,"stem":2187,"children":2188},"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",[2189],{"title":2185,"path":2186,"stem":2187},{"title":2191,"path":2192,"stem":2193,"children":2194},"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",[2195],{"title":2191,"path":2192,"stem":2193},{"title":2197,"path":2198,"stem":2199,"children":2200},"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",[2201],{"title":2197,"path":2198,"stem":2199},{"title":2203,"path":2204,"stem":2205,"children":2206},"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",[2207],{"title":2203,"path":2204,"stem":2205},{"title":2209,"path":2210,"stem":2211,"children":2212,"page":-1},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[2213,2214,2220,2226],{"title":2209,"path":2210,"stem":2211},{"title":2215,"path":2216,"stem":2217,"children":2218},"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",[2219],{"title":2215,"path":2216,"stem":2217},{"title":2221,"path":2222,"stem":2223,"children":2224},"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",[2225],{"title":2221,"path":2222,"stem":2223},{"title":2227,"path":2228,"stem":2229,"children":2230},"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",[2231],{"title":2227,"path":2228,"stem":2229},{"title":2233,"path":2234,"stem":2235,"children":2236,"page":-1},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[2237,2238,2244,2250],{"title":2233,"path":2234,"stem":2235},{"title":2239,"path":2240,"stem":2241,"children":2242},"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",[2243],{"title":2239,"path":2240,"stem":2241},{"title":2245,"path":2246,"stem":2247,"children":2248},"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",[2249],{"title":2245,"path":2246,"stem":2247},{"title":2251,"path":2252,"stem":2253,"children":2254},"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",[2255],{"title":2251,"path":2252,"stem":2253},{"title":2257,"path":2258,"stem":2259,"children":2260},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[2261,2262,2268,2274],{"title":2257,"path":2258,"stem":2259},{"title":2263,"path":2264,"stem":2265,"children":2266},"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",[2267],{"title":2263,"path":2264,"stem":2265},{"title":2269,"path":2270,"stem":2271,"children":2272},"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",[2273],{"title":2269,"path":2270,"stem":2271},{"title":2275,"path":2276,"stem":2277,"children":2278},"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",[2279],{"title":2275,"path":2276,"stem":2277},{"title":2281,"path":2282,"stem":2283,"children":2284},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[2285,2286,2292,2298,2304,2310],{"title":2281,"path":2282,"stem":2283},{"title":2287,"path":2288,"stem":2289,"children":2290},"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",[2291],{"title":2287,"path":2288,"stem":2289},{"title":2293,"path":2294,"stem":2295,"children":2296},"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",[2297],{"title":2293,"path":2294,"stem":2295},{"title":2299,"path":2300,"stem":2301,"children":2302},"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",[2303],{"title":2299,"path":2300,"stem":2301},{"title":2305,"path":2306,"stem":2307,"children":2308},"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",[2309],{"title":2305,"path":2306,"stem":2307},{"title":2311,"path":2312,"stem":2313,"children":2314},"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",[2315],{"title":2311,"path":2312,"stem":2313},{"title":2317,"path":2318,"stem":2319,"children":2320},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[2321,2322,2328,2334],{"title":2317,"path":2318,"stem":2319},{"title":2323,"path":2324,"stem":2325,"children":2326},"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",[2327],{"title":2323,"path":2324,"stem":2325},{"title":2329,"path":2330,"stem":2331,"children":2332},"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",[2333],{"title":2329,"path":2330,"stem":2331},{"title":2335,"path":2336,"stem":2337,"children":2338},"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",[2339],{"title":2335,"path":2336,"stem":2337},{"title":2341,"path":2342,"stem":2343,"children":2344,"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",[2345,2346],{"title":2341,"path":2342,"stem":2343},{"title":2347,"path":2348,"stem":2349,"children":2350},"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",[2351],{"title":2347,"path":2348,"stem":2349},{"title":2353,"path":2354,"stem":2355,"children":2356,"page":-1},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[2357,2360,2390,2420,2450,2474,2504,2528],{"title":2358,"path":2354,"stem":2359},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":2361,"path":2362,"stem":2363,"children":2364,"page":-1},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[2365,2366,2372,2378,2384],{"title":2361,"path":2362,"stem":2363},{"title":2367,"path":2368,"stem":2369,"children":2370},"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",[2371],{"title":2367,"path":2368,"stem":2369},{"title":2373,"path":2374,"stem":2375,"children":2376},"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",[2377],{"title":2373,"path":2374,"stem":2375},{"title":2379,"path":2380,"stem":2381,"children":2382},"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",[2383],{"title":2379,"path":2380,"stem":2381},{"title":2385,"path":2386,"stem":2387,"children":2388},"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",[2389],{"title":2385,"path":2386,"stem":2387},{"title":2391,"path":2392,"stem":2393,"children":2394,"page":-1},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[2395,2396,2402,2408,2414],{"title":2391,"path":2392,"stem":2393},{"title":2397,"path":2398,"stem":2399,"children":2400},"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",[2401],{"title":2397,"path":2398,"stem":2399},{"title":2403,"path":2404,"stem":2405,"children":2406},"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",[2407],{"title":2403,"path":2404,"stem":2405},{"title":2409,"path":2410,"stem":2411,"children":2412},"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",[2413],{"title":2409,"path":2410,"stem":2411},{"title":2415,"path":2416,"stem":2417,"children":2418},"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",[2419],{"title":2415,"path":2416,"stem":2417},{"title":2421,"path":2422,"stem":2423,"children":2424,"page":-1},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[2425,2426,2432,2438,2444],{"title":2421,"path":2422,"stem":2423},{"title":2427,"path":2428,"stem":2429,"children":2430},"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",[2431],{"title":2427,"path":2428,"stem":2429},{"title":2433,"path":2434,"stem":2435,"children":2436},"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",[2437],{"title":2433,"path":2434,"stem":2435},{"title":2439,"path":2440,"stem":2441,"children":2442},"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",[2443],{"title":2439,"path":2440,"stem":2441},{"title":2445,"path":2446,"stem":2447,"children":2448},"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",[2449],{"title":2445,"path":2446,"stem":2447},{"title":2451,"path":2452,"stem":2453,"children":2454,"page":-1},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[2455,2456,2462,2468],{"title":2451,"path":2452,"stem":2453},{"title":2457,"path":2458,"stem":2459,"children":2460},"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",[2461],{"title":2457,"path":2458,"stem":2459},{"title":2463,"path":2464,"stem":2465,"children":2466},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[2467],{"title":2463,"path":2464,"stem":2465},{"title":2469,"path":2470,"stem":2471,"children":2472},"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",[2473],{"title":2469,"path":2470,"stem":2471},{"title":2475,"path":2476,"stem":2477,"children":2478,"page":-1},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[2479,2480,2486,2492,2498],{"title":2475,"path":2476,"stem":2477},{"title":2481,"path":2482,"stem":2483,"children":2484},"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",[2485],{"title":2481,"path":2482,"stem":2483},{"title":2487,"path":2488,"stem":2489,"children":2490},"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",[2491],{"title":2487,"path":2488,"stem":2489},{"title":2493,"path":2494,"stem":2495,"children":2496},"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",[2497],{"title":2493,"path":2494,"stem":2495},{"title":2499,"path":2500,"stem":2501,"children":2502},"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",[2503],{"title":2499,"path":2500,"stem":2501},{"title":2505,"path":2506,"stem":2507,"children":2508,"page":-1},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[2509,2510,2516,2522],{"title":2505,"path":2506,"stem":2507},{"title":2511,"path":2512,"stem":2513,"children":2514},"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",[2515],{"title":2511,"path":2512,"stem":2513},{"title":2517,"path":2518,"stem":2519,"children":2520},"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",[2521],{"title":2517,"path":2518,"stem":2519},{"title":2523,"path":2524,"stem":2525,"children":2526},"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",[2527],{"title":2523,"path":2524,"stem":2525},{"title":5,"path":2164,"stem":2166,"children":2529},[2530,2531,2537,2543],{"title":5,"path":2164,"stem":2166},{"title":2532,"path":2533,"stem":2534,"children":2535},"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",[2536],{"title":2532,"path":2533,"stem":2534},{"title":2538,"path":2539,"stem":2540,"children":2541},"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",[2542],{"title":2538,"path":2539,"stem":2540},{"title":2544,"path":2545,"stem":2546,"children":2547},"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",[2548],{"title":2544,"path":2545,"stem":2546},{"title":2550,"path":2551,"stem":2552,"children":2553,"page":-1},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[2554,2557,2575,2599,2635,2659,2689,2719],{"title":2555,"path":2551,"stem":2556},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":2558,"path":2559,"stem":2560,"children":2561,"page":-1},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[2562,2563,2569],{"title":2558,"path":2559,"stem":2560},{"title":2564,"path":2565,"stem":2566,"children":2567},"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",[2568],{"title":2564,"path":2565,"stem":2566},{"title":2570,"path":2571,"stem":2572,"children":2573},"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",[2574],{"title":2570,"path":2571,"stem":2572},{"title":2576,"path":2577,"stem":2578,"children":2579},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[2580,2581,2587,2593],{"title":2576,"path":2577,"stem":2578},{"title":2582,"path":2583,"stem":2584,"children":2585},"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",[2586],{"title":2582,"path":2583,"stem":2584},{"title":2588,"path":2589,"stem":2590,"children":2591},"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",[2592],{"title":2588,"path":2589,"stem":2590},{"title":2594,"path":2595,"stem":2596,"children":2597},"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",[2598],{"title":2594,"path":2595,"stem":2596},{"title":2600,"path":2601,"stem":2602,"children":2603,"page":-1},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[2604,2605,2611,2617,2623,2629],{"title":2600,"path":2601,"stem":2602},{"title":2606,"path":2607,"stem":2608,"children":2609},"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",[2610],{"title":2606,"path":2607,"stem":2608},{"title":2612,"path":2613,"stem":2614,"children":2615},"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",[2616],{"title":2612,"path":2613,"stem":2614},{"title":2618,"path":2619,"stem":2620,"children":2621},"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",[2622],{"title":2618,"path":2619,"stem":2620},{"title":2624,"path":2625,"stem":2626,"children":2627},"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",[2628],{"title":2624,"path":2625,"stem":2626},{"title":2630,"path":2631,"stem":2632,"children":2633},"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",[2634],{"title":2630,"path":2631,"stem":2632},{"title":2636,"path":2637,"stem":2638,"children":2639,"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",[2640,2641,2647,2653],{"title":2636,"path":2637,"stem":2638},{"title":2642,"path":2643,"stem":2644,"children":2645},"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",[2646],{"title":2642,"path":2643,"stem":2644},{"title":2648,"path":2649,"stem":2650,"children":2651},"Global Exception Handlers for Consistent API Responses","\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",[2652],{"title":2648,"path":2649,"stem":2650},{"title":2654,"path":2655,"stem":2656,"children":2657},"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",[2658],{"title":2654,"path":2655,"stem":2656},{"title":2660,"path":2661,"stem":2662,"children":2663,"page":-1},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[2664,2665,2671,2677,2683],{"title":2660,"path":2661,"stem":2662},{"title":2666,"path":2667,"stem":2668,"children":2669},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[2670],{"title":2666,"path":2667,"stem":2668},{"title":2672,"path":2673,"stem":2674,"children":2675},"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",[2676],{"title":2672,"path":2673,"stem":2674},{"title":2678,"path":2679,"stem":2680,"children":2681},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[2682],{"title":2678,"path":2679,"stem":2680},{"title":2684,"path":2685,"stem":2686,"children":2687},"Middleware vs Dependencies: When to Use Which","\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",[2688],{"title":2684,"path":2685,"stem":2686},{"title":2690,"path":2691,"stem":2692,"children":2693,"page":-1},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[2694,2695,2701,2707,2713],{"title":2690,"path":2691,"stem":2692},{"title":2696,"path":2697,"stem":2698,"children":2699},"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",[2700],{"title":2696,"path":2697,"stem":2698},{"title":2702,"path":2703,"stem":2704,"children":2705},"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",[2706],{"title":2702,"path":2703,"stem":2704},{"title":2708,"path":2709,"stem":2710,"children":2711},"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",[2712],{"title":2708,"path":2709,"stem":2710},{"title":2714,"path":2715,"stem":2716,"children":2717},"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",[2718],{"title":2714,"path":2715,"stem":2716},{"title":2720,"path":2721,"stem":2722,"children":2723,"page":-1},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[2724,2725,2731,2737],{"title":2720,"path":2721,"stem":2722},{"title":2726,"path":2727,"stem":2728,"children":2729},"How a Request Flows Through FastAPI","\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",[2730],{"title":2726,"path":2727,"stem":2728},{"title":2732,"path":2733,"stem":2734,"children":2735},"Response Model and Serialization Order","\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",[2736],{"title":2732,"path":2733,"stem":2734},{"title":2738,"path":2739,"stem":2740,"children":2741},"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",[2742],{"title":2738,"path":2739,"stem":2740},[2154,2154],1784588202410]