[{"data":1,"prerenderedAt":3246},["ShallowReactive",2],{"page-\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002F":3,"nav":2672,"surround-\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002F":3245},{"id":4,"title":5,"body":6,"dateModified":2638,"datePublished":2638,"description":2639,"extension":2640,"faq":2641,"howto":2655,"meta":2656,"navigation":460,"path":2667,"seo":2668,"stem":2669,"type":2670,"__hash__":2671},"content\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex.md","Request Validation Patterns in FastAPI",{"type":7,"value":8,"toc":2618},"minimark",[9,13,20,84,107,117,321,326,341,345,353,425,428,745,752,759,788,793,796,820,979,982,988,1009,1015,1018,1070,1073,1118,1121,1149,1168,1184,1198,1204,1208,1211,1268,1279,1406,1412,1427,1431,1437,1565,1571,1586,1679,1685,1689,1798,1840,1865,1869,1878,1881,1902,1915,1927,1945,1949,1960,1975,1979,1982,1999,2167,2184,2206,2210,2225,2253,2270,2289,2295,2312,2316,2415,2418,2422,2425,2435,2444,2458,2462,2489,2495,2517,2540,2560,2564,2614],[10,11,5],"h1",{"id":12},"request-validation-patterns-in-fastapi",[14,15,16],"p",{},[17,18,19],"strong",{},"Key takeaways:",[21,22,23,49,55,58,78],"ul",{},[24,25,26,27,31,32,31,35,31,38,31,41,44,45,48],"li",{},"FastAPI infers a parameter's source from its name and its type; explicit ",[28,29,30],"code",{},"Query",", ",[28,33,34],{},"Path",[28,36,37],{},"Body",[28,39,40],{},"Header",[28,42,43],{},"Cookie"," and ",[28,46,47],{},"Form"," markers override the inference.",[24,50,51,54],{},[28,52,53],{},"Annotated"," is where the metadata belongs — it separates \"what this parameter is\" from \"what its default value is\".",[24,56,57],{},"Every parameter is resolved before the handler runs, and all failures are reported together in one 422.",[24,59,60,61,31,64,31,67,31,70,73,74,77],{},"A validation error has five keys: ",[28,62,63],{},"type",[28,65,66],{},"loc",[28,68,69],{},"msg",[28,71,72],{},"input"," and, when a constraint carries parameters, ",[28,75,76],{},"ctx",".",[24,79,80,81,83],{},"The first element of ",[28,82,66],{}," names the source, which is the fastest way to diagnose \"the client sent it but FastAPI says it is missing\".",[14,85,86,87,92,93,97,98,102,103,77],{},"This guide sits under ",[88,89,91],"a",{"href":90},"\u002Fadvanced-pydantic-validation-serialization\u002F","Advanced Pydantic Validation and Serialization"," and covers the boundary layer: everything that happens between an HTTP request arriving and your handler's first line executing. The pages beneath it go deeper on ",[88,94,96],{"href":95},"\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002F","query, path and body parameter validation",", on ",[88,99,101],{"href":100},"\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002F","file uploads and form data",", and on the ",[88,104,106],{"href":105},"\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002F","optional versus nullable distinction",[14,108,109,110,44,113,116],{},"Everything on this page was produced by executing the code shown. The transcripts come from ",[28,111,112],{},"_verify\u002Fexamples\u002Fval-422-anatomy.py",[28,114,115],{},"_verify\u002Fexamples\u002Fval-param-sources.py"," running on FastAPI 0.139.2, Pydantic 2.13.4 and Python 3.12.",[118,119,120,317],"figure",{},[121,122,130,131,130,135,130,139,130,148,130,155,130,160,130,168,130,174,130,176,130,180,130,183,130,187,130,190,130,194,130,197,130,201,130,208,130,214,130,218,130,222,130,227,130,232,130,236,130,239,130,244,130,248,130,255,130,258,130,261,130,264,130,267,130,274,130,277,130,279,130,282,130,285,130,291,130,296,130,300,130,303,130,309,130,313],"svg",{"viewBox":123,"role":124,"ariaLabelledBy":125,"xmlns":128,"style":129},"0 0 760 340","img",[126,127],"rvp-title","rvp-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[132,133,134],"title",{"id":126},"How FastAPI resolves a request parameter to a source",[136,137,138],"desc",{"id":127},"An incoming request is split into five sources — path, query, header, cookie and body. Each declared handler parameter is matched to one source, validated by pydantic-core, and either accumulated into a 422 error list or passed to the handler.",[140,141],"rect",{"x":142,"y":143,"width":144,"height":145,"rx":146,"style":147},"16","20","150","52","8","fill:#F9FAFB;stroke:currentColor;stroke-width:1.4px",[149,150,154],"text",{"x":151,"y":152,"style":153},"91","42","text-anchor:middle;fill:currentColor;font:600 13px sans-serif","HTTP request",[149,156,159],{"x":151,"y":157,"style":158},"60","text-anchor:middle;fill:currentColor;font:400 11px sans-serif","raw bytes",[140,161],{"x":162,"y":163,"width":164,"height":165,"rx":166,"style":167},"220","12","130","30","6","fill:#FFFFFF;stroke:#00796B;stroke-width:1.5px",[149,169,173],{"x":170,"y":171,"style":172},"285","32","text-anchor:middle;fill:#00796B;font:600 12px sans-serif","path",[140,175],{"x":162,"y":145,"width":164,"height":165,"rx":166,"style":167},[149,177,179],{"x":170,"y":178,"style":172},"72","query",[140,181],{"x":162,"y":182,"width":164,"height":165,"rx":166,"style":167},"92",[149,184,186],{"x":170,"y":185,"style":172},"112","header",[140,188],{"x":162,"y":189,"width":164,"height":165,"rx":166,"style":167},"132",[149,191,193],{"x":170,"y":192,"style":172},"152","cookie",[140,195],{"x":162,"y":196,"width":164,"height":165,"rx":166,"style":167},"172",[149,198,200],{"x":170,"y":199,"style":172},"192","body",[140,202],{"x":203,"y":178,"width":204,"height":205,"rx":206,"style":207},"400","160","82","9","fill:#E0F2F1;stroke:#00796B;stroke-width:1.8px",[149,209,213],{"x":210,"y":211,"style":212},"480","100","text-anchor:middle;fill:#00796B;font:700 13px sans-serif","pydantic-core",[149,215,217],{"x":210,"y":216,"style":158},"120","coerce + constrain",[149,219,221],{"x":210,"y":220,"style":158},"138","per parameter",[140,223],{"x":224,"y":143,"width":225,"height":157,"rx":146,"style":226},"600","144","fill:#FFFFFF;stroke:currentColor;stroke-width:1.4px",[149,228,231],{"x":229,"y":230,"style":153},"672","44","422 response",[149,233,235],{"x":229,"y":234,"style":158},"63","all errors listed",[140,237],{"x":224,"y":144,"width":225,"height":157,"rx":146,"style":238},"fill:#E0F2F1;stroke:#00796B;stroke-width:1.6px",[149,240,243],{"x":229,"y":241,"style":242},"174","text-anchor:middle;fill:#00796B;font:600 13px sans-serif","your handler",[149,245,247],{"x":229,"y":246,"style":158},"193","typed arguments",[249,250],"line",{"x1":251,"y1":252,"x2":253,"y2":165,"style":254},"166","46","214","stroke:currentColor;stroke-width:1.4px",[249,256],{"x1":251,"y1":252,"x2":253,"y2":257,"style":254},"67",[249,259],{"x1":251,"y1":252,"x2":253,"y2":260,"style":254},"107",[249,262],{"x1":251,"y1":252,"x2":253,"y2":263,"style":254},"147",[249,265],{"x1":251,"y1":252,"x2":253,"y2":266,"style":254},"187",[249,268],{"x1":269,"y1":270,"x2":271,"y2":272,"style":273},"350","27","396","90","stroke:#00796B;stroke-width:1.4px",[249,275],{"x1":269,"y1":257,"x2":271,"y2":276,"style":273},"102",[249,278],{"x1":269,"y1":260,"x2":271,"y2":185,"style":273},[249,280],{"x1":269,"y1":263,"x2":271,"y2":281,"style":273},"124",[249,283],{"x1":269,"y1":266,"x2":271,"y2":284,"style":273},"136",[249,286],{"x1":287,"y1":211,"x2":288,"y2":289,"style":290},"560","594","62","stroke:#00796B;stroke-width:1.8px",[292,293],"polygon",{"points":294,"style":295},"590,58 598,60 592,66","fill:#00796B",[249,297],{"x1":287,"y1":298,"x2":288,"y2":299,"style":290},"126","168",[292,301],{"points":302,"style":295},"590,164 598,170 591,174",[149,304,308],{"x":305,"y":306,"style":307},"380","250","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","Every parameter is resolved before the handler is called, so failures",[149,310,312],{"x":305,"y":311,"style":307},"270","from different sources accumulate into a single 422 error list.",[149,314,316],{"x":305,"y":315,"style":307},"300","The first element of each error's loc names the source it came from.",[318,319,320],"figcaption",{},"The five request sources feed one validation pass. Either every parameter resolves and the handler runs, or the accumulated errors become a 422.",[322,323,325],"h2",{"id":324},"prerequisites","Prerequisites",[14,327,328,329,331,332,336,337,77],{},"You should be comfortable with Pydantic v2 models and ",[28,330,53],{},". Familiarity with ",[88,333,335],{"href":334},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","dependency injection strategies"," helps, because dependencies are resolved by the same machinery and their parameters obey the same source rules. If you are still on Pydantic v1, the error shapes below will not match yours — start with the ",[88,338,340],{"href":339},"\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002F","Pydantic V2 migration guide",[322,342,344],{"id":343},"how-fastapi-assigns-a-parameter-to-a-source","How FastAPI Assigns a Parameter to a Source",[14,346,347,348,352],{},"FastAPI inspects your handler's signature once, at import time, and builds a ",[349,350,351],"em",{},"dependant"," — a tree describing where each argument must come from. The inference has a short set of rules, applied in order:",[354,355,356,370,386,392],"ol",{},[24,357,358,361,362,365,366,369],{},[17,359,360],{},"Does the parameter name appear in the path template?"," If the route is ",[28,363,364],{},"\u002Fusers\u002F{user_id}\u002Forders"," and the handler takes ",[28,367,368],{},"user_id",", it is a path parameter. Path parameters are always required; there is no such thing as an optional path segment in a single route.",[24,371,372,375,376,31,379,31,382,385],{},[17,373,374],{},"Is there an explicit marker in the annotation?"," ",[28,377,378],{},"Annotated[str, Query()]",[28,380,381],{},"Annotated[str, Header()]",[28,383,384],{},"Annotated[bytes, File()]"," and friends win over everything that follows.",[24,387,388,391],{},[17,389,390],{},"Is the type a Pydantic model, a dataclass, or another \"complex\" type?"," Then it is read from the request body as JSON.",[24,393,394,397,398,31,401,31,404,31,407,31,410,31,413,416,417,420,421,424],{},[17,395,396],{},"Otherwise it is a scalar"," — ",[28,399,400],{},"int",[28,402,403],{},"str",[28,405,406],{},"float",[28,408,409],{},"bool",[28,411,412],{},"UUID",[28,414,415],{},"datetime",", an ",[28,418,419],{},"Enum",", or a ",[28,422,423],{},"list"," of those — and it comes from the query string.",[14,426,427],{},"That last rule is the one that surprises people. Adding a bool flag next to a body model silently makes it a query parameter:",[429,430,435],"pre",{"className":431,"code":432,"language":433,"meta":434,"style":434},"language-python shiki shiki-themes github-light-high-contrast","from typing import Annotated, Any\n\nfrom fastapi import Body, FastAPI, Query\nfrom pydantic import BaseModel\n\napp = FastAPI()\n\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n\n@app.get(\"\u002Fusers\u002F{user_id}\u002Forders\")\nasync def list_orders(user_id: int, status: str = \"open\", limit: int = 10) -> dict[str, Any]:\n    # user_id matches the path template -> path parameter.\n    # status and limit do not -> query parameters, because they are scalars.\n    return {\"user_id\": user_id, \"status\": status, \"limit\": limit}\n\n\n@app.post(\"\u002Fitems\u002F\")\nasync def create_item(item: Item, dry_run: bool = False) -> dict[str, Any]:\n    # item is a BaseModel -> body. dry_run is a scalar not in the path -> query string.\n    return {\"dry_run\": dry_run, \"item\": item.model_dump()}\n","python","",[28,436,437,455,462,475,488,493,505,510,515,535,544,553,558,563,585,631,638,644,671,676,681,694,720,726],{"__ignoreMap":434},[438,439,441,445,449,452],"span",{"class":249,"line":440},1,[438,442,444],{"class":443},"sTJeM","from",[438,446,448],{"class":447},"sigWx"," typing ",[438,450,451],{"class":443},"import",[438,453,454],{"class":447}," Annotated, Any\n",[438,456,458],{"class":249,"line":457},2,[438,459,461],{"emptyLinePlaceholder":460},true,"\n",[438,463,465,467,470,472],{"class":249,"line":464},3,[438,466,444],{"class":443},[438,468,469],{"class":447}," fastapi ",[438,471,451],{"class":443},[438,473,474],{"class":447}," Body, FastAPI, Query\n",[438,476,478,480,483,485],{"class":249,"line":477},4,[438,479,444],{"class":443},[438,481,482],{"class":447}," pydantic ",[438,484,451],{"class":443},[438,486,487],{"class":447}," BaseModel\n",[438,489,491],{"class":249,"line":490},5,[438,492,461],{"emptyLinePlaceholder":460},[438,494,496,499,502],{"class":249,"line":495},6,[438,497,498],{"class":447},"app ",[438,500,501],{"class":443},"=",[438,503,504],{"class":447}," FastAPI()\n",[438,506,508],{"class":249,"line":507},7,[438,509,461],{"emptyLinePlaceholder":460},[438,511,513],{"class":249,"line":512},8,[438,514,461],{"emptyLinePlaceholder":460},[438,516,518,521,525,528,532],{"class":249,"line":517},9,[438,519,520],{"class":443},"class",[438,522,524],{"class":523},"sV4o_"," Item",[438,526,527],{"class":447},"(",[438,529,531],{"class":530},"sacAq","BaseModel",[438,533,534],{"class":447},"):\n",[438,536,538,541],{"class":249,"line":537},10,[438,539,540],{"class":447},"    name: ",[438,542,543],{"class":530},"str\n",[438,545,547,550],{"class":249,"line":546},11,[438,548,549],{"class":447},"    price: ",[438,551,552],{"class":530},"float\n",[438,554,556],{"class":249,"line":555},12,[438,557,461],{"emptyLinePlaceholder":460},[438,559,561],{"class":249,"line":560},13,[438,562,461],{"emptyLinePlaceholder":460},[438,564,566,570,572,576,579,582],{"class":249,"line":565},14,[438,567,569],{"class":568},"s3dhs","@app.get",[438,571,527],{"class":447},[438,573,575],{"class":574},"sYEJz","\"\u002Fusers\u002F",[438,577,578],{"class":443},"{user_id}",[438,580,581],{"class":574},"\u002Forders\"",[438,583,584],{"class":447},")\n",[438,586,588,591,594,597,600,602,605,607,610,613,616,618,620,623,626,628],{"class":249,"line":587},15,[438,589,590],{"class":443},"async",[438,592,593],{"class":443}," def",[438,595,596],{"class":568}," list_orders",[438,598,599],{"class":447},"(user_id: ",[438,601,400],{"class":530},[438,603,604],{"class":447},", status: ",[438,606,403],{"class":530},[438,608,609],{"class":443}," =",[438,611,612],{"class":574}," \"open\"",[438,614,615],{"class":447},", limit: ",[438,617,400],{"class":530},[438,619,609],{"class":443},[438,621,622],{"class":530}," 10",[438,624,625],{"class":447},") -> dict[",[438,627,403],{"class":530},[438,629,630],{"class":447},", Any]:\n",[438,632,634],{"class":249,"line":633},16,[438,635,637],{"class":636},"sFeEa","    # user_id matches the path template -> path parameter.\n",[438,639,641],{"class":249,"line":640},17,[438,642,643],{"class":636},"    # status and limit do not -> query parameters, because they are scalars.\n",[438,645,647,650,653,656,659,662,665,668],{"class":249,"line":646},18,[438,648,649],{"class":443},"    return",[438,651,652],{"class":447}," {",[438,654,655],{"class":574},"\"user_id\"",[438,657,658],{"class":447},": user_id, ",[438,660,661],{"class":574},"\"status\"",[438,663,664],{"class":447},": status, ",[438,666,667],{"class":574},"\"limit\"",[438,669,670],{"class":447},": limit}\n",[438,672,674],{"class":249,"line":673},19,[438,675,461],{"emptyLinePlaceholder":460},[438,677,679],{"class":249,"line":678},20,[438,680,461],{"emptyLinePlaceholder":460},[438,682,684,687,689,692],{"class":249,"line":683},21,[438,685,686],{"class":568},"@app.post",[438,688,527],{"class":447},[438,690,691],{"class":574},"\"\u002Fitems\u002F\"",[438,693,584],{"class":447},[438,695,697,699,701,704,707,709,711,714,716,718],{"class":249,"line":696},22,[438,698,590],{"class":443},[438,700,593],{"class":443},[438,702,703],{"class":568}," create_item",[438,705,706],{"class":447},"(item: Item, dry_run: ",[438,708,409],{"class":530},[438,710,609],{"class":443},[438,712,713],{"class":530}," False",[438,715,625],{"class":447},[438,717,403],{"class":530},[438,719,630],{"class":447},[438,721,723],{"class":249,"line":722},23,[438,724,725],{"class":636},"    # item is a BaseModel -> body. dry_run is a scalar not in the path -> query string.\n",[438,727,729,731,733,736,739,742],{"class":249,"line":728},24,[438,730,649],{"class":443},[438,732,652],{"class":447},[438,734,735],{"class":574},"\"dry_run\"",[438,737,738],{"class":447},": dry_run, ",[438,740,741],{"class":574},"\"item\"",[438,743,744],{"class":447},": item.model_dump()}\n",[14,746,747,748,751],{},"Running this against the real app — the transcript below is the genuine output of ",[28,749,750],{},"verify.py val-param-sources",", not a paraphrase:",[429,753,757],{"className":754,"code":756,"language":149,"meta":434},[755],"language-text","$ GET \u002Fusers\u002F7\u002Forders?status=shipped&limit=2\n200 OK\n{\n  \"user_id\": 7,\n  \"status\": \"shipped\",\n  \"limit\": 2\n}\n\n$ GET \u002Fusers\u002Fseven\u002Forders\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"path\",\n        \"user_id\"\n      ],\n      \"msg\": \"Input should be a valid integer, unable to parse string as an integer\",\n      \"input\": \"seven\"\n    }\n  ]\n}\n\n$ POST \u002Fitems\u002F?dry_run=true  {\"name\": \"Widget\", \"price\": 9.5}\n200 OK\n{\n  \"dry_run\": true,\n  \"item\": {\n    \"name\": \"Widget\",\n    \"price\": 9.5\n  }\n}\n",[28,758,756],{"__ignoreMap":434},[14,760,761,764,765,768,769,772,773,775,776,779,780,783,784,787],{},[28,762,763],{},"dry_run"," had to be sent in the query string. A client that put ",[28,766,767],{},"\"dry_run\": true"," inside the JSON object would have got a ",[28,770,771],{},"200"," with ",[28,774,763],{}," still ",[28,777,778],{},"false",", because the extra key is simply ignored by ",[28,781,782],{},"Item"," under Pydantic's default ",[28,785,786],{},"extra=\"ignore\"",". Silent, and expensive to debug — which is why the \"scalars go to the query string\" rule is worth memorizing rather than rediscovering.",[789,790,792],"h3",{"id":791},"overriding-the-inference","Overriding the inference",[14,794,795],{},"Three markers change the default:",[21,797,798,804,810],{},[24,799,800,803],{},[28,801,802],{},"Body()"," moves a scalar into the JSON body.",[24,805,806,809],{},[28,807,808],{},"Query()"," on a Pydantic model (FastAPI 0.115 and later) reads the model's fields from the query string.",[24,811,812,815,816,819],{},[28,813,814],{},"Body(embed=True)"," nests a single model under its parameter name instead of making the model ",[349,817,818],{},"be"," the body.",[429,821,823],{"className":431,"code":822,"language":433,"meta":434,"style":434},"class Filters(BaseModel):\n    \"\"\"A Pydantic model explicitly bound to the query string (FastAPI 0.115+).\"\"\"\n\n    status: str = \"any\"\n    limit: int = 10\n\n\n@app.post(\"\u002Fitems\u002Fembedded\")\nasync def create_embedded(item: Annotated[Item, Body(embed=True)]) -> dict[str, Any]:\n    # embed=True nests the model under its parameter name in the body.\n    return {\"item\": item.model_dump()}\n\n\n@app.get(\"\u002Fsearch\")\nasync def search(filters: Annotated[Filters, Query()]) -> dict[str, Any]:\n    # Explicitly bound to the query string, so a model can describe many query params at once.\n    return filters.model_dump()\n",[28,824,825,838,843,847,859,871,875,879,890,917,922,932,936,940,951,967,972],{"__ignoreMap":434},[438,826,827,829,832,834,836],{"class":249,"line":440},[438,828,520],{"class":443},[438,830,831],{"class":523}," Filters",[438,833,527],{"class":447},[438,835,531],{"class":530},[438,837,534],{"class":447},[438,839,840],{"class":249,"line":457},[438,841,842],{"class":574},"    \"\"\"A Pydantic model explicitly bound to the query string (FastAPI 0.115+).\"\"\"\n",[438,844,845],{"class":249,"line":464},[438,846,461],{"emptyLinePlaceholder":460},[438,848,849,852,854,856],{"class":249,"line":477},[438,850,851],{"class":447},"    status: ",[438,853,403],{"class":530},[438,855,609],{"class":443},[438,857,858],{"class":574}," \"any\"\n",[438,860,861,864,866,868],{"class":249,"line":490},[438,862,863],{"class":447},"    limit: ",[438,865,400],{"class":530},[438,867,609],{"class":443},[438,869,870],{"class":530}," 10\n",[438,872,873],{"class":249,"line":495},[438,874,461],{"emptyLinePlaceholder":460},[438,876,877],{"class":249,"line":507},[438,878,461],{"emptyLinePlaceholder":460},[438,880,881,883,885,888],{"class":249,"line":512},[438,882,686],{"class":568},[438,884,527],{"class":447},[438,886,887],{"class":574},"\"\u002Fitems\u002Fembedded\"",[438,889,584],{"class":447},[438,891,892,894,896,899,902,905,907,910,913,915],{"class":249,"line":517},[438,893,590],{"class":443},[438,895,593],{"class":443},[438,897,898],{"class":568}," create_embedded",[438,900,901],{"class":447},"(item: Annotated[Item, Body(",[438,903,904],{"class":523},"embed",[438,906,501],{"class":443},[438,908,909],{"class":530},"True",[438,911,912],{"class":447},")]) -> dict[",[438,914,403],{"class":530},[438,916,630],{"class":447},[438,918,919],{"class":249,"line":537},[438,920,921],{"class":636},"    # embed=True nests the model under its parameter name in the body.\n",[438,923,924,926,928,930],{"class":249,"line":546},[438,925,649],{"class":443},[438,927,652],{"class":447},[438,929,741],{"class":574},[438,931,744],{"class":447},[438,933,934],{"class":249,"line":555},[438,935,461],{"emptyLinePlaceholder":460},[438,937,938],{"class":249,"line":560},[438,939,461],{"emptyLinePlaceholder":460},[438,941,942,944,946,949],{"class":249,"line":565},[438,943,569],{"class":568},[438,945,527],{"class":447},[438,947,948],{"class":574},"\"\u002Fsearch\"",[438,950,584],{"class":447},[438,952,953,955,957,960,963,965],{"class":249,"line":587},[438,954,590],{"class":443},[438,956,593],{"class":443},[438,958,959],{"class":568}," search",[438,961,962],{"class":447},"(filters: Annotated[Filters, Query()]) -> dict[",[438,964,403],{"class":530},[438,966,630],{"class":447},[438,968,969],{"class":249,"line":633},[438,970,971],{"class":636},"    # Explicitly bound to the query string, so a model can describe many query params at once.\n",[438,973,974,976],{"class":249,"line":640},[438,975,649],{"class":443},[438,977,978],{"class":447}," filters.model_dump()\n",[14,980,981],{},"Real output for both:",[429,983,986],{"className":984,"code":985,"language":149,"meta":434},[755],"$ POST \u002Fitems\u002Fembedded  {\"name\": \"Widget\", \"price\": 9.5}\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"missing\",\n      \"loc\": [\n        \"body\",\n        \"item\"\n      ],\n      \"msg\": \"Field required\",\n      \"input\": null\n    }\n  ]\n}\n\n$ POST \u002Fitems\u002Fembedded  {\"item\": {\"name\": \"Widget\", \"price\": 9.5}}\n200 OK\n{\n  \"item\": {\n    \"name\": \"Widget\",\n    \"price\": 9.5\n  }\n}\n\n$ GET \u002Fsearch?status=open&limit=3\n200 OK\n{\n  \"status\": \"open\",\n  \"limit\": 3\n}\n\n$ GET \u002Fsearch?limit=notanint\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"query\",\n        \"limit\"\n      ],\n      \"msg\": \"Input should be a valid integer, unable to parse string as an integer\",\n      \"input\": \"notanint\"\n    }\n  ]\n}\n",[28,987,985],{"__ignoreMap":434},[14,989,990,991,993,994,997,998,1001,1002,1005,1006,1008],{},"Note the ",[28,992,66],{}," on the failing ",[28,995,996],{},"\u002Fsearch"," request: ",[28,999,1000],{},"[\"query\", \"limit\"]",", not ",[28,1003,1004],{},"[\"body\", \"filters\", \"limit\"]",". Binding a model to the query string does not nest it. That makes ",[28,1007,808],{},"-bound models a clean way to share a filter contract between endpoints without every handler repeating six keyword arguments.",[322,1010,1012,1013],{"id":1011},"the-role-of-annotated","The Role of ",[28,1014,53],{},[14,1016,1017],{},"There are two ways to attach metadata to a parameter. The older one puts it in the default slot:",[429,1019,1021],{"className":431,"code":1020,"language":433,"meta":434,"style":434},"# Legacy style: the marker occupies the default value.\nasync def search(q: str = Query(min_length=2, default=\"all\")): ...\n",[28,1022,1023,1028],{"__ignoreMap":434},[438,1024,1025],{"class":249,"line":440},[438,1026,1027],{"class":636},"# Legacy style: the marker occupies the default value.\n",[438,1029,1030,1032,1034,1036,1039,1041,1043,1046,1049,1051,1054,1056,1059,1061,1064,1067],{"class":249,"line":457},[438,1031,590],{"class":443},[438,1033,593],{"class":443},[438,1035,959],{"class":568},[438,1037,1038],{"class":447},"(q: ",[438,1040,403],{"class":530},[438,1042,609],{"class":443},[438,1044,1045],{"class":447}," Query(",[438,1047,1048],{"class":523},"min_length",[438,1050,501],{"class":443},[438,1052,1053],{"class":530},"2",[438,1055,31],{"class":447},[438,1057,1058],{"class":523},"default",[438,1060,501],{"class":443},[438,1062,1063],{"class":574},"\"all\"",[438,1065,1066],{"class":447},")): ",[438,1068,1069],{"class":530},"...\n",[14,1071,1072],{},"The modern one puts it in the type:",[429,1074,1076],{"className":431,"code":1075,"language":433,"meta":434,"style":434},"# Annotated style: the metadata is part of the type, the default is a real default.\nasync def search(q: Annotated[str, Query(min_length=2)] = \"all\"): ...\n",[28,1077,1078,1083],{"__ignoreMap":434},[438,1079,1080],{"class":249,"line":440},[438,1081,1082],{"class":636},"# Annotated style: the metadata is part of the type, the default is a real default.\n",[438,1084,1085,1087,1089,1091,1094,1096,1099,1101,1103,1105,1108,1110,1113,1116],{"class":249,"line":457},[438,1086,590],{"class":443},[438,1088,593],{"class":443},[438,1090,959],{"class":568},[438,1092,1093],{"class":447},"(q: Annotated[",[438,1095,403],{"class":530},[438,1097,1098],{"class":447},", Query(",[438,1100,1048],{"class":523},[438,1102,501],{"class":443},[438,1104,1053],{"class":530},[438,1106,1107],{"class":447},")] ",[438,1109,501],{"class":443},[438,1111,1112],{"class":574}," \"all\"",[438,1114,1115],{"class":447},"): ",[438,1117,1069],{"class":530},[14,1119,1120],{},"These are not merely stylistic alternatives. Four practical differences:",[14,1122,1123,1126,1127,1130,1131,1134,1135,1137,1138,1140,1141,31,1143,1145,1146,1148],{},[17,1124,1125],{},"The function stays callable."," With the legacy form, calling ",[28,1128,1129],{},"search()"," directly in a unit test binds ",[28,1132,1133],{},"q"," to a ",[28,1136,30],{}," object, not to ",[28,1139,1063],{},". With ",[28,1142,53],{},[28,1144,1133],{}," really is ",[28,1147,1063],{},", so the handler is an ordinary function you can call without a client.",[14,1150,1151,375,1154,1157,1158,1160,1161,1163,1164,1167],{},[17,1152,1153],{},"Type checkers see the truth.",[28,1155,1156],{},"q: str = Query(...)"," declares a ",[28,1159,403],{}," whose default is not a ",[28,1162,403],{},". Static analysers either special-case FastAPI or complain. ",[28,1165,1166],{},"Annotated[str, Query(...)] = \"all\""," is internally consistent.",[14,1169,1170,375,1173,1175,1176,1180,1181,1183],{},[17,1171,1172],{},"The metadata is reusable.",[28,1174,53],{}," types can be aliased and shared, which is the whole subject of ",[88,1177,1179],{"href":1178},"\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002F","Annotated dependencies and reusable types",". A ",[28,1182,30],{}," in a default slot is stuck on that one parameter.",[14,1185,1186,375,1189,1192,1193,1197],{},[17,1187,1188],{},"It composes with Pydantic's own metadata.",[28,1190,1191],{},"Annotated[int, Field(ge=1), SomeAfterValidator]"," stacks cleanly, which is how the ",[88,1194,1196],{"href":1195},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002F","reusable custom validators"," approach works.",[14,1199,1200,1201,1203],{},"The default-value form still works and is not deprecated. New code should use ",[28,1202,53],{}," anyway; it is the form FastAPI's own documentation now leads with, and it is the only form that survives being refactored into a shared type.",[322,1205,1207],{"id":1206},"validation-order","Validation Order",[14,1209,1210],{},"The sequence for a single request is fixed:",[354,1212,1213,1219,1233,1239,1249,1255],{},[24,1214,1215,1218],{},[17,1216,1217],{},"Routing."," Starlette matches the path. A failure here is a 404, never a 422 — the request never reaches parameter validation.",[24,1220,1221,1224,1225,1228,1229,1232],{},[17,1222,1223],{},"Dependency resolution."," Dependencies are resolved depth-first. Their own parameters are validated by the same rules, so a ",[28,1226,1227],{},"Query(min_length=2)"," inside a dependency produces a ",[28,1230,1231],{},"[\"query\", …]"," error exactly as it would in the handler.",[24,1234,1235,1238],{},[17,1236,1237],{},"Parameter resolution."," Path, query, header and cookie values are read as strings from the request and coerced to their declared types.",[24,1240,1241,1244,1245,1248],{},[17,1242,1243],{},"Body parsing."," The body is read and JSON-decoded (or form-decoded). A syntactically invalid JSON body fails here with a ",[28,1246,1247],{},"json_invalid"," error, and no field-level errors follow, because there is nothing to walk.",[24,1250,1251,1254],{},[17,1252,1253],{},"Accumulation."," All errors from steps 2–4 are collected into one list.",[24,1256,1257,1260,1261,1264,1265,77],{},[17,1258,1259],{},"Dispatch."," If the list is empty, the handler is called. If not, FastAPI raises ",[28,1262,1263],{},"RequestValidationError"," and its default handler returns 422 with the list as ",[28,1266,1267],{},"detail",[14,1269,1270,1271,1274,1275,1278],{},"Two consequences are worth internalising. First, ",[17,1272,1273],{},"a handler never runs with partially valid arguments"," — there is no \"it got past validation but the field was wrong\" state. Second, ",[17,1276,1277],{},"errors from different sources arrive together",". One request can fail its path, query and body simultaneously and get a single response describing all three:",[429,1280,1282],{"className":431,"code":1281,"language":433,"meta":434,"style":434},"@app.post(\"\u002Forders\u002F{tenant}\u002Faudit\")\nasync def audited_order(\n    tenant: Annotated[str, Path(min_length=2)],\n    trace: Annotated[str, Query(min_length=4)],\n    order: Order,\n    reason: Annotated[str, Body(min_length=5)],\n) -> dict[str, Any]:\n    return {\"tenant\": tenant, \"trace\": trace, \"reason\": reason}\n",[28,1283,1284,1301,1313,1332,1350,1355,1374,1382],{"__ignoreMap":434},[438,1285,1286,1288,1290,1293,1296,1299],{"class":249,"line":440},[438,1287,686],{"class":568},[438,1289,527],{"class":447},[438,1291,1292],{"class":574},"\"\u002Forders\u002F",[438,1294,1295],{"class":443},"{tenant}",[438,1297,1298],{"class":574},"\u002Faudit\"",[438,1300,584],{"class":447},[438,1302,1303,1305,1307,1310],{"class":249,"line":457},[438,1304,590],{"class":443},[438,1306,593],{"class":443},[438,1308,1309],{"class":568}," audited_order",[438,1311,1312],{"class":447},"(\n",[438,1314,1315,1318,1320,1323,1325,1327,1329],{"class":249,"line":464},[438,1316,1317],{"class":447},"    tenant: Annotated[",[438,1319,403],{"class":530},[438,1321,1322],{"class":447},", Path(",[438,1324,1048],{"class":523},[438,1326,501],{"class":443},[438,1328,1053],{"class":530},[438,1330,1331],{"class":447},")],\n",[438,1333,1334,1337,1339,1341,1343,1345,1348],{"class":249,"line":477},[438,1335,1336],{"class":447},"    trace: Annotated[",[438,1338,403],{"class":530},[438,1340,1098],{"class":447},[438,1342,1048],{"class":523},[438,1344,501],{"class":443},[438,1346,1347],{"class":530},"4",[438,1349,1331],{"class":447},[438,1351,1352],{"class":249,"line":490},[438,1353,1354],{"class":447},"    order: Order,\n",[438,1356,1357,1360,1362,1365,1367,1369,1372],{"class":249,"line":495},[438,1358,1359],{"class":447},"    reason: Annotated[",[438,1361,403],{"class":530},[438,1363,1364],{"class":447},", Body(",[438,1366,1048],{"class":523},[438,1368,501],{"class":443},[438,1370,1371],{"class":530},"5",[438,1373,1331],{"class":447},[438,1375,1376,1378,1380],{"class":249,"line":507},[438,1377,625],{"class":447},[438,1379,403],{"class":530},[438,1381,630],{"class":447},[438,1383,1384,1386,1388,1391,1394,1397,1400,1403],{"class":249,"line":512},[438,1385,649],{"class":443},[438,1387,652],{"class":447},[438,1389,1390],{"class":574},"\"tenant\"",[438,1392,1393],{"class":447},": tenant, ",[438,1395,1396],{"class":574},"\"trace\"",[438,1398,1399],{"class":447},": trace, ",[438,1401,1402],{"class":574},"\"reason\"",[438,1404,1405],{"class":447},": reason}\n",[429,1407,1410],{"className":1408,"code":1409,"language":149,"meta":434},[755],"$ POST \u002Forders\u002Ft\u002Faudit?trace=xy  {\"order\": {\"customer_id\": 1, \"items\": []}, \"reason\": \"oops\"}\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"string_too_short\",\n      \"loc\": [\n        \"path\",\n        \"tenant\"\n      ],\n      \"msg\": \"String should have at least 2 characters\",\n      \"input\": \"t\",\n      \"ctx\": {\n        \"min_length\": 2\n      }\n    },\n    {\n      \"type\": \"string_too_short\",\n      \"loc\": [\n        \"query\",\n        \"trace\"\n      ],\n      \"msg\": \"String should have at least 4 characters\",\n      \"input\": \"xy\",\n      \"ctx\": {\n        \"min_length\": 4\n      }\n    },\n    {\n      \"type\": \"string_too_short\",\n      \"loc\": [\n        \"body\",\n        \"reason\"\n      ],\n      \"msg\": \"String should have at least 5 characters\",\n      \"input\": \"oops\",\n      \"ctx\": {\n        \"min_length\": 5\n      }\n    }\n  ]\n}\n",[28,1411,1409],{"__ignoreMap":434},[14,1413,1414,1415,1418,1419,1422,1423,77],{},"What does ",[349,1416,1417],{},"not"," happen at this stage is response validation. A ",[28,1420,1421],{},"response_model"," mismatch is caught after the handler returns and surfaces as a 500, because a response your own code produced that violates your own contract is a server bug, not a client one. That asymmetry is covered under ",[88,1424,1426],{"href":1425},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002F","response model and serialization order",[322,1428,1430],{"id":1429},"anatomy-of-the-422","Anatomy of the 422",[14,1432,1433,1434,1436],{},"Every entry in ",[28,1435,1267],{}," has the same shape. Here is one endpoint declaring all four non-body sources at once, and what a request that violates every one of them returns:",[429,1438,1440],{"className":431,"code":1439,"language":433,"meta":434,"style":434},"@app.get(\"\u002Floc-demo\u002F{item_id}\")\nasync def loc_demo(\n    item_id: Annotated[int, Path(ge=1)],\n    q: Annotated[str, Query(min_length=3)],\n    x_api_key: Annotated[str, Header()],\n    session: Annotated[str, Cookie()],\n) -> dict[str, Any]:\n    return {\"item_id\": item_id, \"q\": q, \"x_api_key\": x_api_key, \"session\": session}\n",[28,1441,1442,1459,1470,1489,1507,1517,1527,1535],{"__ignoreMap":434},[438,1443,1444,1446,1448,1451,1454,1457],{"class":249,"line":440},[438,1445,569],{"class":568},[438,1447,527],{"class":447},[438,1449,1450],{"class":574},"\"\u002Floc-demo\u002F",[438,1452,1453],{"class":443},"{item_id}",[438,1455,1456],{"class":574},"\"",[438,1458,584],{"class":447},[438,1460,1461,1463,1465,1468],{"class":249,"line":457},[438,1462,590],{"class":443},[438,1464,593],{"class":443},[438,1466,1467],{"class":568}," loc_demo",[438,1469,1312],{"class":447},[438,1471,1472,1475,1477,1479,1482,1484,1487],{"class":249,"line":464},[438,1473,1474],{"class":447},"    item_id: Annotated[",[438,1476,400],{"class":530},[438,1478,1322],{"class":447},[438,1480,1481],{"class":523},"ge",[438,1483,501],{"class":443},[438,1485,1486],{"class":530},"1",[438,1488,1331],{"class":447},[438,1490,1491,1494,1496,1498,1500,1502,1505],{"class":249,"line":477},[438,1492,1493],{"class":447},"    q: Annotated[",[438,1495,403],{"class":530},[438,1497,1098],{"class":447},[438,1499,1048],{"class":523},[438,1501,501],{"class":443},[438,1503,1504],{"class":530},"3",[438,1506,1331],{"class":447},[438,1508,1509,1512,1514],{"class":249,"line":490},[438,1510,1511],{"class":447},"    x_api_key: Annotated[",[438,1513,403],{"class":530},[438,1515,1516],{"class":447},", Header()],\n",[438,1518,1519,1522,1524],{"class":249,"line":495},[438,1520,1521],{"class":447},"    session: Annotated[",[438,1523,403],{"class":530},[438,1525,1526],{"class":447},", Cookie()],\n",[438,1528,1529,1531,1533],{"class":249,"line":507},[438,1530,625],{"class":447},[438,1532,403],{"class":530},[438,1534,630],{"class":447},[438,1536,1537,1539,1541,1544,1547,1550,1553,1556,1559,1562],{"class":249,"line":512},[438,1538,649],{"class":443},[438,1540,652],{"class":447},[438,1542,1543],{"class":574},"\"item_id\"",[438,1545,1546],{"class":447},": item_id, ",[438,1548,1549],{"class":574},"\"q\"",[438,1551,1552],{"class":447},": q, ",[438,1554,1555],{"class":574},"\"x_api_key\"",[438,1557,1558],{"class":447},": x_api_key, ",[438,1560,1561],{"class":574},"\"session\"",[438,1563,1564],{"class":447},": session}\n",[429,1566,1569],{"className":1567,"code":1568,"language":149,"meta":434},[755],"$ GET \u002Floc-demo\u002Fabc?q=a\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"path\",\n        \"item_id\"\n      ],\n      \"msg\": \"Input should be a valid integer, unable to parse string as an integer\",\n      \"input\": \"abc\"\n    },\n    {\n      \"type\": \"string_too_short\",\n      \"loc\": [\n        \"query\",\n        \"q\"\n      ],\n      \"msg\": \"String should have at least 3 characters\",\n      \"input\": \"a\",\n      \"ctx\": {\n        \"min_length\": 3\n      }\n    },\n    {\n      \"type\": \"missing\",\n      \"loc\": [\n        \"header\",\n        \"x-api-key\"\n      ],\n      \"msg\": \"Field required\",\n      \"input\": null\n    },\n    {\n      \"type\": \"missing\",\n      \"loc\": [\n        \"cookie\",\n        \"session\"\n      ],\n      \"msg\": \"Field required\",\n      \"input\": null\n    }\n  ]\n}\n",[28,1570,1568],{"__ignoreMap":434},[14,1572,1573,1574,31,1576,31,1578,44,1580,1582,1583,1585],{},"That single response carries a real example of the ",[28,1575,173],{},[28,1577,179],{},[28,1579,186],{},[28,1581,193],{}," prefixes. The ",[28,1584,200],{}," prefix needs a payload, so here is the fifth, with nesting:",[429,1587,1589],{"className":431,"code":1588,"language":433,"meta":434,"style":434},"class LineItem(BaseModel):\n    sku: str = Field(min_length=3)\n    quantity: int = Field(gt=0)\n\n\nclass Order(BaseModel):\n    customer_id: int\n    items: list[LineItem]\n",[28,1590,1591,1604,1624,1645,1649,1653,1666,1674],{"__ignoreMap":434},[438,1592,1593,1595,1598,1600,1602],{"class":249,"line":440},[438,1594,520],{"class":443},[438,1596,1597],{"class":523}," LineItem",[438,1599,527],{"class":447},[438,1601,531],{"class":530},[438,1603,534],{"class":447},[438,1605,1606,1609,1611,1613,1616,1618,1620,1622],{"class":249,"line":457},[438,1607,1608],{"class":447},"    sku: ",[438,1610,403],{"class":530},[438,1612,609],{"class":443},[438,1614,1615],{"class":447}," Field(",[438,1617,1048],{"class":523},[438,1619,501],{"class":443},[438,1621,1504],{"class":530},[438,1623,584],{"class":447},[438,1625,1626,1629,1631,1633,1635,1638,1640,1643],{"class":249,"line":464},[438,1627,1628],{"class":447},"    quantity: ",[438,1630,400],{"class":530},[438,1632,609],{"class":443},[438,1634,1615],{"class":447},[438,1636,1637],{"class":523},"gt",[438,1639,501],{"class":443},[438,1641,1642],{"class":530},"0",[438,1644,584],{"class":447},[438,1646,1647],{"class":249,"line":477},[438,1648,461],{"emptyLinePlaceholder":460},[438,1650,1651],{"class":249,"line":490},[438,1652,461],{"emptyLinePlaceholder":460},[438,1654,1655,1657,1660,1662,1664],{"class":249,"line":495},[438,1656,520],{"class":443},[438,1658,1659],{"class":523}," Order",[438,1661,527],{"class":447},[438,1663,531],{"class":530},[438,1665,534],{"class":447},[438,1667,1668,1671],{"class":249,"line":507},[438,1669,1670],{"class":447},"    customer_id: ",[438,1672,1673],{"class":530},"int\n",[438,1675,1676],{"class":249,"line":512},[438,1677,1678],{"class":447},"    items: list[LineItem]\n",[429,1680,1683],{"className":1681,"code":1682,"language":149,"meta":434},[755],"$ POST \u002Forders\u002F  {\"customer_id\": \"n\u002Fa\", \"items\": [{\"sku\": \"ab\", \"quantity\": 0}]}\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"body\",\n        \"customer_id\"\n      ],\n      \"msg\": \"Input should be a valid integer, unable to parse string as an integer\",\n      \"input\": \"n\u002Fa\"\n    },\n    {\n      \"type\": \"string_too_short\",\n      \"loc\": [\n        \"body\",\n        \"items\",\n        0,\n        \"sku\"\n      ],\n      \"msg\": \"String should have at least 3 characters\",\n      \"input\": \"ab\",\n      \"ctx\": {\n        \"min_length\": 3\n      }\n    },\n    {\n      \"type\": \"greater_than\",\n      \"loc\": [\n        \"body\",\n        \"items\",\n        0,\n        \"quantity\"\n      ],\n      \"msg\": \"Input should be greater than 0\",\n      \"input\": 0,\n      \"ctx\": {\n        \"gt\": 0\n      }\n    }\n  ]\n}\n",[28,1684,1682],{"__ignoreMap":434},[789,1686,1688],{"id":1687},"the-five-keys","The five keys",[1690,1691,1692,1708],"table",{},[1693,1694,1695],"thead",{},[1696,1697,1698,1702,1705],"tr",{},[1699,1700,1701],"th",{},"Key",[1699,1703,1704],{},"What it is",[1699,1706,1707],{},"Use it for",[1709,1710,1711,1743,1755,1770,1782],"tbody",{},[1696,1712,1713,1718,1740],{},[1714,1715,1716],"td",{},[28,1717,63],{},[1714,1719,1720,1721,31,1724,31,1727,31,1730,31,1733,31,1736,1739],{},"Stable machine identifier for the failure: ",[28,1722,1723],{},"missing",[28,1725,1726],{},"int_parsing",[28,1728,1729],{},"string_too_short",[28,1731,1732],{},"greater_than",[28,1734,1735],{},"literal_error",[28,1737,1738],{},"string_pattern_mismatch",", …",[1714,1741,1742],{},"Branching in client code; mapping to your own error codes",[1696,1744,1745,1749,1752],{},[1714,1746,1747],{},[28,1748,66],{},[1714,1750,1751],{},"Path to the offending value: source prefix, then keys and list indices",[1714,1753,1754],{},"Highlighting the right form field, including inside arrays",[1696,1756,1757,1761,1764],{},[1714,1758,1759],{},[28,1760,69],{},[1714,1762,1763],{},"Human-readable English sentence generated by Pydantic",[1714,1765,1766,1767,1769],{},"Showing to developers; ",[17,1768,1417],{}," for parsing",[1696,1771,1772,1776,1779],{},[1714,1773,1774],{},[28,1775,72],{},[1714,1777,1778],{},"The value that was rejected, exactly as received",[1714,1780,1781],{},"Debugging and log correlation",[1696,1783,1784,1788,1791],{},[1714,1785,1786],{},[28,1787,76],{},[1714,1789,1790],{},"The constraint's own parameters, present only when the constraint has any",[1714,1792,1793,1794,1797],{},"Rendering your own message: \"must be at least ",[28,1795,1796],{},"{min_length}"," characters\"",[14,1799,1800,1801,1804,1805,1808,1809,1812,1813,1816,1817,1829,1830,1832,1833,1836,1837,77],{},"Two details are easy to miss. ",[17,1802,1803],{},"Header names are normalised",": the Python parameter ",[28,1806,1807],{},"x_api_key"," reports as ",[28,1810,1811],{},"\"x-api-key\"",", because that is the wire name FastAPI derived by replacing underscores with hyphens. If your client is genuinely sending ",[28,1814,1815],{},"X_Api_Key",", that mismatch is the bug. And ",[17,1818,1819,1821,1822,1824,1825,1828],{},[28,1820,72],{}," on a ",[28,1823,1723],{}," error is ",[28,1826,1827],{},"null"," for scalars but the whole enclosing object for body fields"," — compare the two ",[28,1831,1723],{}," entries above with the ones in the ",[88,1834,1835],{"href":105},"optional versus nullable guide",", where a missing body field reports ",[28,1838,1839],{},"\"input\": {}",[14,1841,1842,1844,1845,1848,1849,1852,1853,1856,1857,1860,1861,1864],{},[28,1843,66],{}," elements are a mix of strings and integers. ",[28,1846,1847],{},"[\"body\", \"items\", 0, \"sku\"]"," means the ",[28,1850,1851],{},"sku"," field of the first element of the ",[28,1854,1855],{},"items"," array. Client code that renders these must handle both types; joining them with ",[28,1858,1859],{},"\".\""," unconditionally will produce ",[28,1862,1863],{},"body.items.0.sku",", which is fine for a log line and wrong for a JSON Pointer.",[322,1866,1868],{"id":1867},"what-happens-after-validation-fails","What Happens After Validation Fails",[14,1870,1871,1872,1874,1875,77],{},"The 422 is not special-cased anywhere in the routing layer. When the accumulated error list is non-empty, FastAPI raises ",[28,1873,1263],{},", which carries the list and the raw body it was reading. That exception is routed to a registered exception handler exactly like any other, and FastAPI installs a default one that returns ",[28,1876,1877],{},"JSONResponse(status_code=422, content={\"detail\": exc.errors()})",[14,1879,1880],{},"Three consequences follow from that design, and they are what make the 422 tractable in production.",[14,1882,1883,1886,1887,1889,1890,44,1892,1894,1895,1897,1898,77],{},[17,1884,1885],{},"You can replace the envelope without touching a single route."," Registering your own handler for ",[28,1888,1263],{}," reshapes every validation failure in the application at once. Most teams do this to wrap errors in whatever envelope the rest of their API uses, and to add a correlation id. Keep ",[28,1891,63],{},[28,1893,66],{}," in your output — they are the only fields a client can act on programmatically — and feel free to drop ",[28,1896,72],{},", which can contain data you would rather not echo. The mechanics are in ",[88,1899,1901],{"href":1900},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002F","customising validation error responses",[14,1903,1904,1907,1908,1910,1911,77],{},[17,1905,1906],{},"You can log validation failures centrally."," A single handler is the natural place to emit a structured log line per rejected request, with the route, the error types and the ",[28,1909,66],{}," paths. Aggregated, that stream tells you which client is sending malformed data and which of your constraints are too strict, which is far more actionable than a 4xx rate. Pair it with the request id from ",[88,1912,1914],{"href":1913},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002F","structured JSON logging with request ids",[14,1916,1917,1923,1924,1926],{},[17,1918,1919,1920,1922],{},"Beware of echoing ",[28,1921,72],{}," verbatim."," For a body error, ",[28,1925,72],{}," can be the entire enclosing object — which may include a password or a token the client sent to the wrong endpoint. If your logs or your error responses are less trusted than your request bodies, strip or redact it in the handler.",[14,1928,1929,1930,1933,1934,1936,1937,1940,1941,77],{},"One more distinction worth holding onto: 422 means ",[349,1931,1932],{},"the request was well-formed but did not satisfy the contract",". A malformed JSON body is also a 422 in FastAPI (with a single ",[28,1935,1247],{}," error), while a ",[28,1938,1939],{},"HTTPException(400)"," you raise yourself means \"the request satisfied the schema but is wrong for reasons the schema cannot express\" — a duplicate email, an expired coupon. Keeping that boundary clean is what lets clients distinguish \"fix your serializer\" from \"fix your data\", and it is the same boundary argued for in ",[88,1942,1944],{"href":1943},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002F","HTTPException vs custom exception classes",[322,1946,1948],{"id":1947},"validation-inside-dependencies","Validation Inside Dependencies",[14,1950,1951,1952,1955,1956,1959],{},"A dependency's own parameters are validated by the same machinery, which is easy to forget because the failure surfaces on the route rather than in the dependency. A ",[28,1953,1954],{},"Header()"," parameter inside an auth dependency that is missing produces a ",[28,1957,1958],{},"[\"header\", …]"," entry in the route's 422, indistinguishable in shape from one declared on the handler itself.",[14,1961,1962,1963,1966,1967,1970,1971,77],{},"That is usually what you want: pagination, filtering and tenancy headers can be declared once in a dependency and reused across dozens of routes, and their constraints appear in each route's OpenAPI document automatically. It has one trap. A dependency that raises ",[28,1964,1965],{},"HTTPException(401)"," short-circuits the request immediately, whereas a dependency whose ",[349,1968,1969],{},"parameters"," fail validation contributes to the accumulated 422 instead. So a request that is both unauthenticated and malformed can return either 401 or 422 depending on whether the auth check runs before or after the failing parameter is resolved — which is an implementation detail you should not depend on. If your clients need deterministic ordering, do the authentication check in middleware, where it definitively precedes parameter validation; the trade-offs are in ",[88,1972,1974],{"href":1973},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F","middleware vs dependencies",[322,1976,1978],{"id":1977},"testing-request-validation","Testing Request Validation",[14,1980,1981],{},"Validation contracts deserve tests, because they are the part of your API clients actually collide with. Two patterns cover most of it.",[14,1983,1984,1994,1995,1998],{},[17,1985,1986,1987,44,1989,1991,1992,77],{},"Assert on ",[28,1988,63],{},[28,1990,66],{},", never on ",[28,1993,69],{}," Pydantic's English strings change between minor releases; the type identifiers are stable. A test that asserts ",[28,1996,1997],{},"msg == \"String should have at least 3 characters\""," is a test that breaks on upgrade for no reason.",[429,2000,2002],{"className":431,"code":2001,"language":433,"meta":434,"style":434},"from fastapi.testclient import TestClient\n\n\ndef test_short_query_is_rejected():\n    client = TestClient(app)\n    response = client.get(\"\u002Floc-demo\u002F1\", params={\"q\": \"a\"})\n    assert response.status_code == 422\n    errors = {(e[\"type\"], tuple(e[\"loc\"])) for e in response.json()[\"detail\"]}\n    assert (\"string_too_short\", (\"query\", \"q\")) in errors\n",[28,2003,2004,2016,2020,2024,2035,2045,2079,2093,2139],{"__ignoreMap":434},[438,2005,2006,2008,2011,2013],{"class":249,"line":440},[438,2007,444],{"class":443},[438,2009,2010],{"class":447}," fastapi.testclient ",[438,2012,451],{"class":443},[438,2014,2015],{"class":447}," TestClient\n",[438,2017,2018],{"class":249,"line":457},[438,2019,461],{"emptyLinePlaceholder":460},[438,2021,2022],{"class":249,"line":464},[438,2023,461],{"emptyLinePlaceholder":460},[438,2025,2026,2029,2032],{"class":249,"line":477},[438,2027,2028],{"class":443},"def",[438,2030,2031],{"class":568}," test_short_query_is_rejected",[438,2033,2034],{"class":447},"():\n",[438,2036,2037,2040,2042],{"class":249,"line":490},[438,2038,2039],{"class":447},"    client ",[438,2041,501],{"class":443},[438,2043,2044],{"class":447}," TestClient(app)\n",[438,2046,2047,2050,2052,2055,2058,2060,2063,2065,2068,2070,2073,2076],{"class":249,"line":495},[438,2048,2049],{"class":447},"    response ",[438,2051,501],{"class":443},[438,2053,2054],{"class":447}," client.get(",[438,2056,2057],{"class":574},"\"\u002Floc-demo\u002F1\"",[438,2059,31],{"class":447},[438,2061,2062],{"class":523},"params",[438,2064,501],{"class":443},[438,2066,2067],{"class":447},"{",[438,2069,1549],{"class":574},[438,2071,2072],{"class":447},": ",[438,2074,2075],{"class":574},"\"a\"",[438,2077,2078],{"class":447},"})\n",[438,2080,2081,2084,2087,2090],{"class":249,"line":507},[438,2082,2083],{"class":443},"    assert",[438,2085,2086],{"class":447}," response.status_code ",[438,2088,2089],{"class":443},"==",[438,2091,2092],{"class":530}," 422\n",[438,2094,2095,2098,2100,2103,2106,2109,2112,2115,2118,2121,2124,2127,2130,2133,2136],{"class":249,"line":512},[438,2096,2097],{"class":447},"    errors ",[438,2099,501],{"class":443},[438,2101,2102],{"class":447}," {(e[",[438,2104,2105],{"class":574},"\"type\"",[438,2107,2108],{"class":447},"], ",[438,2110,2111],{"class":530},"tuple",[438,2113,2114],{"class":447},"(e[",[438,2116,2117],{"class":574},"\"loc\"",[438,2119,2120],{"class":447},"])) ",[438,2122,2123],{"class":443},"for",[438,2125,2126],{"class":447}," e ",[438,2128,2129],{"class":443},"in",[438,2131,2132],{"class":447}," response.json()[",[438,2134,2135],{"class":574},"\"detail\"",[438,2137,2138],{"class":447},"]}\n",[438,2140,2141,2143,2146,2149,2152,2155,2157,2159,2162,2164],{"class":249,"line":517},[438,2142,2083],{"class":443},[438,2144,2145],{"class":447}," (",[438,2147,2148],{"class":574},"\"string_too_short\"",[438,2150,2151],{"class":447},", (",[438,2153,2154],{"class":574},"\"query\"",[438,2156,31],{"class":447},[438,2158,1549],{"class":574},[438,2160,2161],{"class":447},")) ",[438,2163,2129],{"class":443},[438,2165,2166],{"class":447}," errors\n",[14,2168,2169,2175,2176,2180,2181,77],{},[17,2170,2171,2172,77],{},"Drive dependencies out of the way with ",[28,2173,2174],{},"dependency_overrides"," If an endpoint's validation is entangled with an authentication dependency, override the dependency so the test exercises the parameter contract rather than the auth flow. That mechanism is covered in ",[88,2177,2179],{"href":2178},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","overriding dependencies in tests",", and the reusable-alias form that makes it painless is in ",[88,2182,2183],{"href":1178},"annotated dependencies and reusable types",[14,2185,2186,2187,2190,2191,2194,2195,772,2198,2201,2202,77],{},"For async endpoints, ",[28,2188,2189],{},"TestClient"," still works — it runs the app on its own event loop — but if your test needs to ",[28,2192,2193],{},"await"," alongside the request, use ",[28,2196,2197],{},"httpx.AsyncClient",[28,2199,2200],{},"ASGITransport",", which is the same transport the transcripts on this page came from. The trade-offs are in ",[88,2203,2205],{"href":2204},"\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002F","TestClient vs httpx AsyncClient",[322,2207,2209],{"id":2208},"failure-modes-and-how-to-diagnose-them","Failure Modes and How to Diagnose Them",[14,2211,2212,2215,2216,2218,2219,2221,2222,2224],{},[17,2213,2214],{},"\"The client sends it, FastAPI says it is missing.\""," Read the ",[28,2217,66],{}," prefix. If it says ",[28,2220,179],{}," and your client put the value in the JSON body, the parameter is a scalar and FastAPI routed it to the query string. If it says ",[28,2223,200],{}," and the name is your parameter name rather than a field name, you have two body parameters and FastAPI has embedded them both.",[14,2226,2227,375,2230,2233,2234,2237,2238,2241,2242,2244,2245,2248,2249,2252],{},[17,2228,2229],{},"\"My alias is not being applied.\"",[28,2231,2232],{},"Query(alias=\"customerId\")"," changes the ",[349,2235,2236],{},"wire"," name only. Sending ",[28,2239,2240],{},"customer_id"," then fails with ",[28,2243,1723],{}," on ",[28,2246,2247],{},"customerId",". This is deliberate — the alias ",[349,2250,2251],{},"is"," the contract — but it reads as a bug the first time.",[14,2254,2255,2261,2262,2265,2266,2269],{},[17,2256,2257,2258,2260],{},"\"I get a 422 with a single ",[28,2259,1247],{}," error and no field details.\""," The body was not parseable JSON. Common causes: a ",[28,2263,2264],{},"Content-Type"," of ",[28,2267,2268],{},"application\u002Fx-www-form-urlencoded"," on a JSON payload, or a trailing comma from a hand-rolled client. Field-level errors cannot exist because no fields were ever extracted.",[14,2271,2272,2275,2276,2278,2279,2282,2283,2286,2287,77],{},[17,2273,2274],{},"\"Extra fields are silently ignored.\""," Pydantic's default is ",[28,2277,786],{},". If a typo'd key should be an error, set ",[28,2280,2281],{},"model_config = ConfigDict(extra=\"forbid\")"," on the model and the 422 gains an ",[28,2284,2285],{},"extra_forbidden"," entry with the offending key in ",[28,2288,66],{},[14,2290,2291,2294],{},[17,2292,2293],{},"\"A required path parameter is optional in my code.\""," It is not. A default on a path parameter is ignored — the segment must be present or the route does not match, producing a 404 rather than a 422.",[14,2296,2297,2300,2301,2303,2304,2306,2307,44,2309,2311],{},[17,2298,2299],{},"\"My 422 shape breaks the mobile client.\""," Do not reshape it ad hoc per endpoint. Install one ",[28,2302,1263],{}," handler that maps the standard structure into your envelope, as described in ",[88,2305,1901],{"href":1900},". Keep ",[28,2308,63],{},[28,2310,66],{}," in whatever you emit; they are the only machine-usable parts.",[322,2313,2315],{"id":2314},"choosing-a-source-a-decision-table","Choosing a Source: a Decision Table",[1690,2317,2318,2331],{},[1693,2319,2320],{},[1696,2321,2322,2325,2328],{},[1699,2323,2324],{},"The value is…",[1699,2326,2327],{},"Put it in",[1699,2329,2330],{},"Why",[1709,2332,2333,2343,2353,2367,2381,2391,2401],{},[1696,2334,2335,2338,2340],{},[1714,2336,2337],{},"An identifier for the resource being addressed",[1714,2339,34],{},[1714,2341,2342],{},"Cacheable, appears in logs and metrics as part of the route",[1696,2344,2345,2348,2350],{},[1714,2346,2347],{},"A filter, sort or pagination control",[1714,2349,30],{},[1714,2351,2352],{},"Bookmarkable, safe to log, works with GET",[1696,2354,2355,2358,2361],{},[1714,2356,2357],{},"The entity being created or replaced",[1714,2359,2360],{},"Body (single model)",[1714,2362,2363,2364,2366],{},"The payload ",[349,2365,2251],{}," the resource representation",[1696,2368,2369,2372,2375],{},[1714,2370,2371],{},"Two or more independent objects",[1714,2373,2374],{},"Body (multiple parameters, auto-embedded)",[1714,2376,2377,2378],{},"Each gets its own key; see the ",[88,2379,2380],{"href":95},"parameter validation guide",[1696,2382,2383,2386,2388],{},[1714,2384,2385],{},"Credentials, tenancy, tracing",[1714,2387,40],{},[1714,2389,2390],{},"Not part of the resource identity; excluded from URLs and caches",[1696,2392,2393,2396,2398],{},[1714,2394,2395],{},"Browser-managed session state",[1714,2397,43],{},[1714,2399,2400],{},"Sent automatically; validate it like any other input",[1696,2402,2403,2406,2409],{},[1714,2404,2405],{},"Binary content or a browser form post",[1714,2407,2408],{},"Form \u002F File",[1714,2410,2411,2412],{},"Multipart cannot coexist with a JSON body — see ",[88,2413,2414],{"href":100},"file uploads and forms",[14,2416,2417],{},"A secret in a query string ends up in access logs, proxy logs and browser history. That alone settles the header-versus-query question for API keys.",[322,2419,2421],{"id":2420},"performance-notes","Performance Notes",[14,2423,2424],{},"Validation cost is real but rarely dominant. Three things actually matter at throughput:",[14,2426,2427,2430,2431,2434],{},[17,2428,2429],{},"Body size beats field count."," Parsing a 2 MB JSON array is dominated by decoding, not by constraint checks. If you accept large collections, bound them with ",[28,2432,2433],{},"Field(max_length=...)"," on the list so an oversized payload is rejected structurally rather than validated element by element.",[14,2436,2437,2440,2441,2443],{},[17,2438,2439],{},"The dependant is built once."," Signature introspection happens at import time, not per request. Declaring twelve ",[28,2442,30],{}," parameters costs nothing at startup-scale and only the per-value coercion at request time.",[14,2445,2446,2449,2450,2453,2454,77],{},[17,2447,2448],{},"Reused annotated types are free."," Aliasing ",[28,2451,2452],{},"Annotated[int, Query(ge=1)]"," and using it on forty routes does not multiply anything; the metadata objects are shared. The cost model is covered more generally in ",[88,2455,2457],{"href":2456},"\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002F","performance optimization for models",[322,2459,2461],{"id":2460},"faq","FAQ",[14,2463,2464,2467,2468,31,2470,31,2472,2474,2475,31,2477,31,2479,31,2481,31,2483,2485,2486,2488],{},[17,2465,2466],{},"How does FastAPI decide whether a parameter is a query parameter or a body field?","\nBy type and by name. If the parameter name appears in the path template it is a path parameter. Otherwise, scalars such as ",[28,2469,400],{},[28,2471,403],{},[28,2473,409],{}," and their lists default to the query string, while Pydantic models default to the JSON body. An explicit ",[28,2476,30],{},[28,2478,34],{},[28,2480,37],{},[28,2482,40],{},[28,2484,43],{}," or ",[28,2487,47],{}," marker overrides the default.",[14,2490,2491,2494],{},[17,2492,2493],{},"Why does my 422 list several errors instead of stopping at the first one?","\nFastAPI resolves every parameter before invoking the handler and accumulates the failures, so a single response can report a bad path segment, a bad query value and a bad body field together. This is deliberate: a client fixing a form wants the whole list, not one error per round trip.",[14,2496,2497,2503,2505,2506,31,2508,31,2510,2513,2514,2516],{},[17,2498,2499,2500,2502],{},"What is the ",[28,2501,76],{}," key in a validation error?",[28,2504,76],{}," carries the machine-readable parameters of the constraint that failed, such as ",[28,2507,1048],{},[28,2509,1637],{},[28,2511,2512],{},"le"," or the expected literal values. It is what you use when you want to render your own error message rather than parse the English ",[28,2515,69],{}," string.",[14,2518,2519,2525,2526,31,2528,31,2530,31,2532,2485,2534,2536,2537,2539],{},[17,2520,2521,2522,2524],{},"Does the ",[28,2523,66],{}," array always start with the parameter source?","\nFor request validation, yes: the first element is ",[28,2527,173],{},[28,2529,179],{},[28,2531,186],{},[28,2533,193],{},[28,2535,200],{},", and the remaining elements walk into the structure. Errors raised by a ",[28,2538,1421],{}," are different — they surface as a 500, not a 422, because a broken response is a server bug.",[14,2541,2542,2553,2554,2556,2557,2559],{},[17,2543,2544,2545,2547,2548,44,2550,2552],{},"Should I use ",[28,2546,53],{}," or default-value syntax for ",[28,2549,30],{},[28,2551,34],{},"?","\nUse ",[28,2555,53],{},". It keeps the metadata in the type rather than in the default slot, which means the real default stays visible, the annotated type can be aliased and reused, and the function remains callable directly in tests without passing a ",[28,2558,30],{}," object.",[322,2561,2563],{"id":2562},"related-reading","Related Reading",[21,2565,2566,2574,2583,2592,2605],{},[24,2567,2568,375,2571,2573],{},[17,2569,2570],{},"Up to the section:",[88,2572,91],{"href":90}," for the full validation and serialization picture.",[24,2575,2576,375,2579,2582],{},[17,2577,2578],{},"Go deeper on parameters:",[88,2580,2581],{"href":95},"Query, Path and Body Parameter Validation"," for constraints, aliases and list parameters.",[24,2584,2585,375,2588,2591],{},[17,2586,2587],{},"Non-JSON payloads:",[88,2589,2590],{"href":100},"Validating File Uploads and Forms"," for multipart, size limits and content-type checks.",[24,2593,2594,375,2597,2600,2601,2604],{},[17,2595,2596],{},"The nullability trap:",[88,2598,2599],{"href":105},"Optional vs Nullable Fields"," for required-but-nullable and ",[28,2602,2603],{},"exclude_unset"," round-tripping.",[24,2606,2607,375,2610,2613],{},[17,2608,2609],{},"Reusable declarations:",[88,2611,2612],{"href":1178},"Annotated Dependencies and Reusable Types"," for turning these declarations into shared aliases.",[2615,2616,2617],"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 .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}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 pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}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);}",{"title":434,"searchDepth":457,"depth":457,"links":2619},[2620,2621,2624,2626,2627,2630,2631,2632,2633,2634,2635,2636,2637],{"id":324,"depth":457,"text":325},{"id":343,"depth":457,"text":344,"children":2622},[2623],{"id":791,"depth":464,"text":792},{"id":1011,"depth":457,"text":2625},"The Role of Annotated",{"id":1206,"depth":457,"text":1207},{"id":1429,"depth":457,"text":1430,"children":2628},[2629],{"id":1687,"depth":464,"text":1688},{"id":1867,"depth":457,"text":1868},{"id":1947,"depth":457,"text":1948},{"id":1977,"depth":457,"text":1978},{"id":2208,"depth":457,"text":2209},{"id":2314,"depth":457,"text":2315},{"id":2420,"depth":457,"text":2421},{"id":2460,"depth":457,"text":2461},{"id":2562,"depth":457,"text":2563},"2026-07-20","How FastAPI picks the path, query, body, header or cookie source for a parameter, the role of Annotated, validation order, and the anatomy of a 422 body.","md",[2642,2644,2646,2649,2652],{"q":2466,"a":2643},"By type and by name. If the parameter name appears in the path template it is a path parameter. Otherwise, scalars such as int, str, bool and their lists default to the query string, while Pydantic models default to the JSON body. An explicit Query, Path, Body, Header, Cookie or Form marker overrides the default.",{"q":2493,"a":2645},"FastAPI resolves every parameter before invoking the handler and accumulates the failures, so a single response can report a bad path segment, a bad query value and a bad body field together. This is deliberate: a client fixing a form wants the whole list, not one error per round trip.",{"q":2647,"a":2648},"What is the ctx key in a validation error?","ctx carries the machine-readable parameters of the constraint that failed, such as min_length, gt, le or the expected literal values. It is what you use when you want to render your own error message rather than parse the English msg string.",{"q":2650,"a":2651},"Does the loc array always start with the parameter source?","For request validation, yes: the first element is path, query, header, cookie or body, and the remaining elements walk into the structure. Errors raised by a response_model are different — they surface as a 500, not a 422, because a broken response is a server bug.",{"q":2653,"a":2654},"Should I use Annotated or default-value syntax for Query and Path?","Use Annotated. It keeps the metadata in the type rather than in the default slot, which means the real default stays visible, the annotated type can be aliased and reused, and the function remains callable directly in tests without passing a Query object.",null,{"slug":2657,"breadcrumb":2658},"request-validation-patterns",[2659,2662,2664],{"label":2660,"path":2661},"Home","\u002F",{"label":2663,"path":90},"Advanced Pydantic Validation & Serialization",{"label":2665,"path":2666},"Request Validation Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002F","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns",{"title":5,"description":2639},"advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex","topic","pm--1i94ZrR-V_Ci4wICqQeWoPJep0quH3Reap-sqVY",[2673,2851,3051],{"title":2674,"path":2675,"stem":2676,"children":2677,"page":-1},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[2678,2680,2710,2734,2758,2782,2818,2839],{"title":91,"path":2675,"stem":2679},"advanced-pydantic-validation-serialization\u002Findex",{"title":2681,"path":2682,"stem":2683,"children":2684,"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",[2685,2686,2692,2698,2704],{"title":2681,"path":2682,"stem":2683},{"title":2687,"path":2688,"stem":2689,"children":2690},"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",[2691],{"title":2687,"path":2688,"stem":2689},{"title":2693,"path":2694,"stem":2695,"children":2696},"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",[2697],{"title":2693,"path":2694,"stem":2695},{"title":2699,"path":2700,"stem":2701,"children":2702},"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",[2703],{"title":2699,"path":2700,"stem":2701},{"title":2705,"path":2706,"stem":2707,"children":2708},"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",[2709],{"title":2705,"path":2706,"stem":2707},{"title":2711,"path":2712,"stem":2713,"children":2714,"page":-1},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[2715,2716,2722,2728],{"title":2711,"path":2712,"stem":2713},{"title":2717,"path":2718,"stem":2719,"children":2720},"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",[2721],{"title":2717,"path":2718,"stem":2719},{"title":2723,"path":2724,"stem":2725,"children":2726},"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",[2727],{"title":2723,"path":2724,"stem":2725},{"title":2729,"path":2730,"stem":2731,"children":2732},"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",[2733],{"title":2729,"path":2730,"stem":2731},{"title":2735,"path":2736,"stem":2737,"children":2738,"page":-1},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[2739,2740,2746,2752],{"title":2735,"path":2736,"stem":2737},{"title":2741,"path":2742,"stem":2743,"children":2744},"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",[2745],{"title":2741,"path":2742,"stem":2743},{"title":2747,"path":2748,"stem":2749,"children":2750},"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",[2751],{"title":2747,"path":2748,"stem":2749},{"title":2753,"path":2754,"stem":2755,"children":2756},"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",[2757],{"title":2753,"path":2754,"stem":2755},{"title":2759,"path":2760,"stem":2761,"children":2762},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[2763,2764,2770,2776],{"title":2759,"path":2760,"stem":2761},{"title":2765,"path":2766,"stem":2767,"children":2768},"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",[2769],{"title":2765,"path":2766,"stem":2767},{"title":2771,"path":2772,"stem":2773,"children":2774},"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",[2775],{"title":2771,"path":2772,"stem":2773},{"title":2777,"path":2778,"stem":2779,"children":2780},"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",[2781],{"title":2777,"path":2778,"stem":2779},{"title":2783,"path":2784,"stem":2785,"children":2786},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[2787,2788,2794,2800,2806,2812],{"title":2783,"path":2784,"stem":2785},{"title":2789,"path":2790,"stem":2791,"children":2792},"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",[2793],{"title":2789,"path":2790,"stem":2791},{"title":2795,"path":2796,"stem":2797,"children":2798},"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",[2799],{"title":2795,"path":2796,"stem":2797},{"title":2801,"path":2802,"stem":2803,"children":2804},"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",[2805],{"title":2801,"path":2802,"stem":2803},{"title":2807,"path":2808,"stem":2809,"children":2810},"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",[2811],{"title":2807,"path":2808,"stem":2809},{"title":2813,"path":2814,"stem":2815,"children":2816},"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",[2817],{"title":2813,"path":2814,"stem":2815},{"title":5,"path":2667,"stem":2669,"children":2819},[2820,2821,2827,2833],{"title":5,"path":2667,"stem":2669},{"title":2822,"path":2823,"stem":2824,"children":2825},"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",[2826],{"title":2822,"path":2823,"stem":2824},{"title":2828,"path":2829,"stem":2830,"children":2831},"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",[2832],{"title":2828,"path":2829,"stem":2830},{"title":2834,"path":2835,"stem":2836,"children":2837},"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",[2838],{"title":2834,"path":2835,"stem":2836},{"title":2840,"path":2841,"stem":2842,"children":2843,"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",[2844,2845],{"title":2840,"path":2841,"stem":2842},{"title":2846,"path":2847,"stem":2848,"children":2849},"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",[2850],{"title":2846,"path":2847,"stem":2848},{"title":2852,"path":2853,"stem":2854,"children":2855,"page":-1},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[2856,2859,2889,2919,2949,2973,3003,3027],{"title":2857,"path":2853,"stem":2858},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":2860,"path":2861,"stem":2862,"children":2863,"page":-1},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[2864,2865,2871,2877,2883],{"title":2860,"path":2861,"stem":2862},{"title":2866,"path":2867,"stem":2868,"children":2869},"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",[2870],{"title":2866,"path":2867,"stem":2868},{"title":2872,"path":2873,"stem":2874,"children":2875},"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",[2876],{"title":2872,"path":2873,"stem":2874},{"title":2878,"path":2879,"stem":2880,"children":2881},"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",[2882],{"title":2878,"path":2879,"stem":2880},{"title":2884,"path":2885,"stem":2886,"children":2887},"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",[2888],{"title":2884,"path":2885,"stem":2886},{"title":2890,"path":2891,"stem":2892,"children":2893,"page":-1},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[2894,2895,2901,2907,2913],{"title":2890,"path":2891,"stem":2892},{"title":2896,"path":2897,"stem":2898,"children":2899},"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",[2900],{"title":2896,"path":2897,"stem":2898},{"title":2902,"path":2903,"stem":2904,"children":2905},"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",[2906],{"title":2902,"path":2903,"stem":2904},{"title":2908,"path":2909,"stem":2910,"children":2911},"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",[2912],{"title":2908,"path":2909,"stem":2910},{"title":2914,"path":2915,"stem":2916,"children":2917},"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",[2918],{"title":2914,"path":2915,"stem":2916},{"title":2920,"path":2921,"stem":2922,"children":2923,"page":-1},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[2924,2925,2931,2937,2943],{"title":2920,"path":2921,"stem":2922},{"title":2926,"path":2927,"stem":2928,"children":2929},"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",[2930],{"title":2926,"path":2927,"stem":2928},{"title":2932,"path":2933,"stem":2934,"children":2935},"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",[2936],{"title":2932,"path":2933,"stem":2934},{"title":2938,"path":2939,"stem":2940,"children":2941},"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",[2942],{"title":2938,"path":2939,"stem":2940},{"title":2944,"path":2945,"stem":2946,"children":2947},"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",[2948],{"title":2944,"path":2945,"stem":2946},{"title":2950,"path":2951,"stem":2952,"children":2953,"page":-1},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[2954,2955,2961,2967],{"title":2950,"path":2951,"stem":2952},{"title":2956,"path":2957,"stem":2958,"children":2959},"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",[2960],{"title":2956,"path":2957,"stem":2958},{"title":2962,"path":2963,"stem":2964,"children":2965},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[2966],{"title":2962,"path":2963,"stem":2964},{"title":2968,"path":2969,"stem":2970,"children":2971},"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",[2972],{"title":2968,"path":2969,"stem":2970},{"title":2974,"path":2975,"stem":2976,"children":2977,"page":-1},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[2978,2979,2985,2991,2997],{"title":2974,"path":2975,"stem":2976},{"title":2980,"path":2981,"stem":2982,"children":2983},"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",[2984],{"title":2980,"path":2981,"stem":2982},{"title":2986,"path":2987,"stem":2988,"children":2989},"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",[2990],{"title":2986,"path":2987,"stem":2988},{"title":2992,"path":2993,"stem":2994,"children":2995},"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",[2996],{"title":2992,"path":2993,"stem":2994},{"title":2998,"path":2999,"stem":3000,"children":3001},"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",[3002],{"title":2998,"path":2999,"stem":3000},{"title":3004,"path":3005,"stem":3006,"children":3007,"page":-1},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[3008,3009,3015,3021],{"title":3004,"path":3005,"stem":3006},{"title":3010,"path":3011,"stem":3012,"children":3013},"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",[3014],{"title":3010,"path":3011,"stem":3012},{"title":3016,"path":3017,"stem":3018,"children":3019},"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",[3020],{"title":3016,"path":3017,"stem":3018},{"title":3022,"path":3023,"stem":3024,"children":3025},"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",[3026],{"title":3022,"path":3023,"stem":3024},{"title":3028,"path":3029,"stem":3030,"children":3031},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[3032,3033,3039,3045],{"title":3028,"path":3029,"stem":3030},{"title":3034,"path":3035,"stem":3036,"children":3037},"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",[3038],{"title":3034,"path":3035,"stem":3036},{"title":3040,"path":3041,"stem":3042,"children":3043},"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",[3044],{"title":3040,"path":3041,"stem":3042},{"title":3046,"path":3047,"stem":3048,"children":3049},"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",[3050],{"title":3046,"path":3047,"stem":3048},{"title":3052,"path":3053,"stem":3054,"children":3055,"page":-1},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[3056,3059,3077,3101,3137,3161,3191,3221],{"title":3057,"path":3053,"stem":3058},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":3060,"path":3061,"stem":3062,"children":3063,"page":-1},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[3064,3065,3071],{"title":3060,"path":3061,"stem":3062},{"title":3066,"path":3067,"stem":3068,"children":3069},"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",[3070],{"title":3066,"path":3067,"stem":3068},{"title":3072,"path":3073,"stem":3074,"children":3075},"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",[3076],{"title":3072,"path":3073,"stem":3074},{"title":3078,"path":3079,"stem":3080,"children":3081},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[3082,3083,3089,3095],{"title":3078,"path":3079,"stem":3080},{"title":3084,"path":3085,"stem":3086,"children":3087},"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",[3088],{"title":3084,"path":3085,"stem":3086},{"title":3090,"path":3091,"stem":3092,"children":3093},"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",[3094],{"title":3090,"path":3091,"stem":3092},{"title":3096,"path":3097,"stem":3098,"children":3099},"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",[3100],{"title":3096,"path":3097,"stem":3098},{"title":3102,"path":3103,"stem":3104,"children":3105,"page":-1},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[3106,3107,3113,3119,3125,3131],{"title":3102,"path":3103,"stem":3104},{"title":3108,"path":3109,"stem":3110,"children":3111},"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",[3112],{"title":3108,"path":3109,"stem":3110},{"title":3114,"path":3115,"stem":3116,"children":3117},"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",[3118],{"title":3114,"path":3115,"stem":3116},{"title":3120,"path":3121,"stem":3122,"children":3123},"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",[3124],{"title":3120,"path":3121,"stem":3122},{"title":3126,"path":3127,"stem":3128,"children":3129},"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",[3130],{"title":3126,"path":3127,"stem":3128},{"title":3132,"path":3133,"stem":3134,"children":3135},"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",[3136],{"title":3132,"path":3133,"stem":3134},{"title":3138,"path":3139,"stem":3140,"children":3141,"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",[3142,3143,3149,3155],{"title":3138,"path":3139,"stem":3140},{"title":3144,"path":3145,"stem":3146,"children":3147},"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",[3148],{"title":3144,"path":3145,"stem":3146},{"title":3150,"path":3151,"stem":3152,"children":3153},"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",[3154],{"title":3150,"path":3151,"stem":3152},{"title":3156,"path":3157,"stem":3158,"children":3159},"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",[3160],{"title":3156,"path":3157,"stem":3158},{"title":3162,"path":3163,"stem":3164,"children":3165,"page":-1},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[3166,3167,3173,3179,3185],{"title":3162,"path":3163,"stem":3164},{"title":3168,"path":3169,"stem":3170,"children":3171},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[3172],{"title":3168,"path":3169,"stem":3170},{"title":3174,"path":3175,"stem":3176,"children":3177},"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",[3178],{"title":3174,"path":3175,"stem":3176},{"title":3180,"path":3181,"stem":3182,"children":3183},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[3184],{"title":3180,"path":3181,"stem":3182},{"title":3186,"path":3187,"stem":3188,"children":3189},"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",[3190],{"title":3186,"path":3187,"stem":3188},{"title":3192,"path":3193,"stem":3194,"children":3195,"page":-1},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[3196,3197,3203,3209,3215],{"title":3192,"path":3193,"stem":3194},{"title":3198,"path":3199,"stem":3200,"children":3201},"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",[3202],{"title":3198,"path":3199,"stem":3200},{"title":3204,"path":3205,"stem":3206,"children":3207},"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",[3208],{"title":3204,"path":3205,"stem":3206},{"title":3210,"path":3211,"stem":3212,"children":3213},"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",[3214],{"title":3210,"path":3211,"stem":3212},{"title":3216,"path":3217,"stem":3218,"children":3219},"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",[3220],{"title":3216,"path":3217,"stem":3218},{"title":3222,"path":3223,"stem":3224,"children":3225,"page":-1},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[3226,3227,3233,3239],{"title":3222,"path":3223,"stem":3224},{"title":3228,"path":3229,"stem":3230,"children":3231},"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",[3232],{"title":3228,"path":3229,"stem":3230},{"title":3234,"path":3235,"stem":3236,"children":3237},"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",[3238],{"title":3234,"path":3235,"stem":3236},{"title":3240,"path":3241,"stem":3242,"children":3243},"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",[3244],{"title":3240,"path":3241,"stem":3242},[2655,2655],1784588202410]