[{"data":1,"prerenderedAt":2572},["ShallowReactive",2],{"page-\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-litestar\u002F":3,"faq-schema-\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-litestar\u002F":2551},{"id":4,"title":5,"body":6,"description":2541,"extension":2542,"meta":2543,"navigation":81,"path":2547,"seo":2548,"stem":2549,"__hash__":2550},"content\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-litestar\u002Findex.md","FastAPI vs Litestar: The Same Endpoint, Two Frameworks",{"type":7,"value":8,"toc":2530},"minimark",[9,13,23,31,36,39,527,534,928,935,939,954,1068,1083,1490,1504,1508,1511,1580,1587,1595,1599,1607,1708,1711,1776,1780,1783,1790,1900,1903,1907,1915,2086,2111,2114,2428,2431,2435,2443,2447,2453,2459,2465,2471,2477,2481,2486,2507,2512,2526],[10,11,5],"h1",{"id":12},"fastapi-vs-litestar-the-same-endpoint-two-frameworks",[14,15,16,17,22],"p",{},"Litestar is the only Python framework that credibly challenges FastAPI on developer experience rather than on raw speed alone. It ships dependency injection with real scoping, msgspec-backed serialization, first-party database plugins, and an opinionated project layout. The question builders actually ask is narrower than \"which is better\": does moving off FastAPI change your compute bill or your shipping speed enough to justify the rewrite? Part of the ",[18,19,21],"a",{"href":20},"\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002F","Setting Up FastAPI"," guide, this page answers that with the same endpoint written twice, then puts numbers against each difference.",[14,24,25,26,30],{},"Both frameworks are ASGI applications. Both run under the same server process, so your ",[18,27,29],{"href":28},"\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Fuvicorn-vs-gunicorn-worker-configuration\u002F","Uvicorn and Gunicorn worker configuration"," carries over unchanged, and both emit an OpenAPI 3.1 document. The differences live in three places: how dependencies resolve, how bytes become objects, and how much of your stack the framework already owns. Everything else is preference.",[32,33,35],"h2",{"id":34},"the-same-endpoint-written-twice","The same endpoint, written twice",[14,37,38],{},"Here is an authenticated read endpoint in FastAPI. Config comes from the environment, the header check is a dependency, and Pydantic owns the response contract.",[40,41,46],"pre",{"className":42,"code":43,"language":44,"meta":45,"style":45},"language-python shiki shiki-themes github-light github-dark","import os\nfrom typing import Annotated\n\nfrom fastapi import Depends, FastAPI, Header, HTTPException, status\nfrom pydantic import BaseModel, Field\n\nAPI_KEY = os.getenv(\"REPORTS_API_KEY\", \"\")\nPAGE_SIZE = int(os.getenv(\"REPORTS_PAGE_SIZE\", \"50\"))\n\n\nclass Report(BaseModel):\n    id: str\n    rows: int = Field(ge=0)\n    currency: str = Field(min_length=3, max_length=3)\n\n\nasync def require_api_key(x_api_key: Annotated[str, Header()]) -> str:\n    if not API_KEY or x_api_key != API_KEY:\n        raise HTTPException(status.HTTP_401_UNAUTHORIZED, \"Invalid API key\")\n    return x_api_key\n\n\napp = FastAPI(title=os.getenv(\"SERVICE_NAME\", \"reports\"))\n\n\n@app.get(\"\u002Freports\u002F{report_id}\", response_model=Report)\nasync def get_report(\n    report_id: str,\n    api_key: Annotated[str, Depends(require_api_key)],\n) -> Report:\n    return Report(\n        id=report_id,\n        rows=PAGE_SIZE,\n        currency=os.getenv(\"REPORT_CURRENCY\", \"USD\"),\n    )\n","python","",[47,48,49,62,76,83,96,109,114,140,165,170,175,194,206,232,264,269,274,299,324,343,352,357,362,391,396,401,428,441,452,463,469,477,488,500,521],"code",{"__ignoreMap":45},[50,51,54,58],"span",{"class":52,"line":53},"line",1,[50,55,57],{"class":56},"szBVR","import",[50,59,61],{"class":60},"sVt8B"," os\n",[50,63,65,68,71,73],{"class":52,"line":64},2,[50,66,67],{"class":56},"from",[50,69,70],{"class":60}," typing ",[50,72,57],{"class":56},[50,74,75],{"class":60}," Annotated\n",[50,77,79],{"class":52,"line":78},3,[50,80,82],{"emptyLinePlaceholder":81},true,"\n",[50,84,86,88,91,93],{"class":52,"line":85},4,[50,87,67],{"class":56},[50,89,90],{"class":60}," fastapi ",[50,92,57],{"class":56},[50,94,95],{"class":60}," Depends, FastAPI, Header, HTTPException, status\n",[50,97,99,101,104,106],{"class":52,"line":98},5,[50,100,67],{"class":56},[50,102,103],{"class":60}," pydantic ",[50,105,57],{"class":56},[50,107,108],{"class":60}," BaseModel, Field\n",[50,110,112],{"class":52,"line":111},6,[50,113,82],{"emptyLinePlaceholder":81},[50,115,117,121,124,127,131,134,137],{"class":52,"line":116},7,[50,118,120],{"class":119},"sj4cs","API_KEY",[50,122,123],{"class":56}," =",[50,125,126],{"class":60}," os.getenv(",[50,128,130],{"class":129},"sZZnC","\"REPORTS_API_KEY\"",[50,132,133],{"class":60},", ",[50,135,136],{"class":129},"\"\"",[50,138,139],{"class":60},")\n",[50,141,143,146,148,151,154,157,159,162],{"class":52,"line":142},8,[50,144,145],{"class":119},"PAGE_SIZE",[50,147,123],{"class":56},[50,149,150],{"class":119}," int",[50,152,153],{"class":60},"(os.getenv(",[50,155,156],{"class":129},"\"REPORTS_PAGE_SIZE\"",[50,158,133],{"class":60},[50,160,161],{"class":129},"\"50\"",[50,163,164],{"class":60},"))\n",[50,166,168],{"class":52,"line":167},9,[50,169,82],{"emptyLinePlaceholder":81},[50,171,173],{"class":52,"line":172},10,[50,174,82],{"emptyLinePlaceholder":81},[50,176,178,181,185,188,191],{"class":52,"line":177},11,[50,179,180],{"class":56},"class",[50,182,184],{"class":183},"sScJk"," Report",[50,186,187],{"class":60},"(",[50,189,190],{"class":183},"BaseModel",[50,192,193],{"class":60},"):\n",[50,195,197,200,203],{"class":52,"line":196},12,[50,198,199],{"class":119},"    id",[50,201,202],{"class":60},": ",[50,204,205],{"class":119},"str\n",[50,207,209,212,215,217,220,224,227,230],{"class":52,"line":208},13,[50,210,211],{"class":60},"    rows: ",[50,213,214],{"class":119},"int",[50,216,123],{"class":56},[50,218,219],{"class":60}," Field(",[50,221,223],{"class":222},"s4XuR","ge",[50,225,226],{"class":56},"=",[50,228,229],{"class":119},"0",[50,231,139],{"class":60},[50,233,235,238,241,243,245,248,250,253,255,258,260,262],{"class":52,"line":234},14,[50,236,237],{"class":60},"    currency: ",[50,239,240],{"class":119},"str",[50,242,123],{"class":56},[50,244,219],{"class":60},[50,246,247],{"class":222},"min_length",[50,249,226],{"class":56},[50,251,252],{"class":119},"3",[50,254,133],{"class":60},[50,256,257],{"class":222},"max_length",[50,259,226],{"class":56},[50,261,252],{"class":119},[50,263,139],{"class":60},[50,265,267],{"class":52,"line":266},15,[50,268,82],{"emptyLinePlaceholder":81},[50,270,272],{"class":52,"line":271},16,[50,273,82],{"emptyLinePlaceholder":81},[50,275,277,280,283,286,289,291,294,296],{"class":52,"line":276},17,[50,278,279],{"class":56},"async",[50,281,282],{"class":56}," def",[50,284,285],{"class":183}," require_api_key",[50,287,288],{"class":60},"(x_api_key: Annotated[",[50,290,240],{"class":119},[50,292,293],{"class":60},", Header()]) -> ",[50,295,240],{"class":119},[50,297,298],{"class":60},":\n",[50,300,302,305,308,311,314,317,320,322],{"class":52,"line":301},18,[50,303,304],{"class":56},"    if",[50,306,307],{"class":56}," not",[50,309,310],{"class":119}," API_KEY",[50,312,313],{"class":56}," or",[50,315,316],{"class":60}," x_api_key ",[50,318,319],{"class":56},"!=",[50,321,310],{"class":119},[50,323,298],{"class":60},[50,325,327,330,333,336,338,341],{"class":52,"line":326},19,[50,328,329],{"class":56},"        raise",[50,331,332],{"class":60}," HTTPException(status.",[50,334,335],{"class":119},"HTTP_401_UNAUTHORIZED",[50,337,133],{"class":60},[50,339,340],{"class":129},"\"Invalid API key\"",[50,342,139],{"class":60},[50,344,346,349],{"class":52,"line":345},20,[50,347,348],{"class":56},"    return",[50,350,351],{"class":60}," x_api_key\n",[50,353,355],{"class":52,"line":354},21,[50,356,82],{"emptyLinePlaceholder":81},[50,358,360],{"class":52,"line":359},22,[50,361,82],{"emptyLinePlaceholder":81},[50,363,365,368,370,373,376,378,381,384,386,389],{"class":52,"line":364},23,[50,366,367],{"class":60},"app ",[50,369,226],{"class":56},[50,371,372],{"class":60}," FastAPI(",[50,374,375],{"class":222},"title",[50,377,226],{"class":56},[50,379,380],{"class":60},"os.getenv(",[50,382,383],{"class":129},"\"SERVICE_NAME\"",[50,385,133],{"class":60},[50,387,388],{"class":129},"\"reports\"",[50,390,164],{"class":60},[50,392,394],{"class":52,"line":393},24,[50,395,82],{"emptyLinePlaceholder":81},[50,397,399],{"class":52,"line":398},25,[50,400,82],{"emptyLinePlaceholder":81},[50,402,404,407,409,412,415,418,420,423,425],{"class":52,"line":403},26,[50,405,406],{"class":183},"@app.get",[50,408,187],{"class":60},[50,410,411],{"class":129},"\"\u002Freports\u002F",[50,413,414],{"class":119},"{report_id}",[50,416,417],{"class":129},"\"",[50,419,133],{"class":60},[50,421,422],{"class":222},"response_model",[50,424,226],{"class":56},[50,426,427],{"class":60},"Report)\n",[50,429,431,433,435,438],{"class":52,"line":430},27,[50,432,279],{"class":56},[50,434,282],{"class":56},[50,436,437],{"class":183}," get_report",[50,439,440],{"class":60},"(\n",[50,442,444,447,449],{"class":52,"line":443},28,[50,445,446],{"class":60},"    report_id: ",[50,448,240],{"class":119},[50,450,451],{"class":60},",\n",[50,453,455,458,460],{"class":52,"line":454},29,[50,456,457],{"class":60},"    api_key: Annotated[",[50,459,240],{"class":119},[50,461,462],{"class":60},", Depends(require_api_key)],\n",[50,464,466],{"class":52,"line":465},30,[50,467,468],{"class":60},") -> Report:\n",[50,470,472,474],{"class":52,"line":471},31,[50,473,348],{"class":56},[50,475,476],{"class":60}," Report(\n",[50,478,480,483,485],{"class":52,"line":479},32,[50,481,482],{"class":222},"        id",[50,484,226],{"class":56},[50,486,487],{"class":60},"report_id,\n",[50,489,491,494,496,498],{"class":52,"line":490},33,[50,492,493],{"class":222},"        rows",[50,495,226],{"class":56},[50,497,145],{"class":119},[50,499,451],{"class":60},[50,501,503,506,508,510,513,515,518],{"class":52,"line":502},34,[50,504,505],{"class":222},"        currency",[50,507,226],{"class":56},[50,509,380],{"class":60},[50,511,512],{"class":129},"\"REPORT_CURRENCY\"",[50,514,133],{"class":60},[50,516,517],{"class":129},"\"USD\"",[50,519,520],{"class":60},"),\n",[50,522,524],{"class":52,"line":523},35,[50,525,526],{"class":60},"    )\n",[14,528,529,530,533],{},"The Litestar version is the same shape with three structural changes: handlers are standalone decorated functions collected by the app object, dependencies are registered by name in a mapping rather than declared in the signature, and the response type is a ",[47,531,532],{},"msgspec.Struct"," instead of a Pydantic model.",[40,535,537],{"className":42,"code":536,"language":44,"meta":45,"style":45},"import os\nfrom typing import Annotated\n\nfrom litestar import Litestar, get\nfrom litestar.di import Provide\nfrom litestar.exceptions import NotAuthorizedException\nfrom litestar.params import Parameter\nfrom msgspec import Struct\n\nAPI_KEY = os.getenv(\"REPORTS_API_KEY\", \"\")\nPAGE_SIZE = int(os.getenv(\"REPORTS_PAGE_SIZE\", \"50\"))\n\n\nclass Report(Struct):\n    id: str\n    rows: int\n    currency: str\n\n\nasync def require_api_key(\n    x_api_key: Annotated[str, Parameter(header=\"X-API-Key\")],\n) -> str:\n    if not API_KEY or x_api_key != API_KEY:\n        raise NotAuthorizedException(\"Invalid API key\")\n    return x_api_key\n\n\n@get(\"\u002Freports\u002F{report_id:str}\")\nasync def get_report(report_id: str, api_key: str) -> Report:\n    return Report(\n        id=report_id,\n        rows=PAGE_SIZE,\n        currency=os.getenv(\"REPORT_CURRENCY\", \"USD\"),\n    )\n\n\napp = Litestar(\n    route_handlers=[get_report],\n    dependencies={\"api_key\": Provide(require_api_key)},\n)\n",[47,538,539,545,555,559,571,583,595,607,619,623,639,657,661,665,678,686,693,699,703,707,717,738,747,765,776,782,786,790,812,832,838,846,856,872,876,880,885,895,906,923],{"__ignoreMap":45},[50,540,541,543],{"class":52,"line":53},[50,542,57],{"class":56},[50,544,61],{"class":60},[50,546,547,549,551,553],{"class":52,"line":64},[50,548,67],{"class":56},[50,550,70],{"class":60},[50,552,57],{"class":56},[50,554,75],{"class":60},[50,556,557],{"class":52,"line":78},[50,558,82],{"emptyLinePlaceholder":81},[50,560,561,563,566,568],{"class":52,"line":85},[50,562,67],{"class":56},[50,564,565],{"class":60}," litestar ",[50,567,57],{"class":56},[50,569,570],{"class":60}," Litestar, get\n",[50,572,573,575,578,580],{"class":52,"line":98},[50,574,67],{"class":56},[50,576,577],{"class":60}," litestar.di ",[50,579,57],{"class":56},[50,581,582],{"class":60}," Provide\n",[50,584,585,587,590,592],{"class":52,"line":111},[50,586,67],{"class":56},[50,588,589],{"class":60}," litestar.exceptions ",[50,591,57],{"class":56},[50,593,594],{"class":60}," NotAuthorizedException\n",[50,596,597,599,602,604],{"class":52,"line":116},[50,598,67],{"class":56},[50,600,601],{"class":60}," litestar.params ",[50,603,57],{"class":56},[50,605,606],{"class":60}," Parameter\n",[50,608,609,611,614,616],{"class":52,"line":142},[50,610,67],{"class":56},[50,612,613],{"class":60}," msgspec ",[50,615,57],{"class":56},[50,617,618],{"class":60}," Struct\n",[50,620,621],{"class":52,"line":167},[50,622,82],{"emptyLinePlaceholder":81},[50,624,625,627,629,631,633,635,637],{"class":52,"line":172},[50,626,120],{"class":119},[50,628,123],{"class":56},[50,630,126],{"class":60},[50,632,130],{"class":129},[50,634,133],{"class":60},[50,636,136],{"class":129},[50,638,139],{"class":60},[50,640,641,643,645,647,649,651,653,655],{"class":52,"line":177},[50,642,145],{"class":119},[50,644,123],{"class":56},[50,646,150],{"class":119},[50,648,153],{"class":60},[50,650,156],{"class":129},[50,652,133],{"class":60},[50,654,161],{"class":129},[50,656,164],{"class":60},[50,658,659],{"class":52,"line":196},[50,660,82],{"emptyLinePlaceholder":81},[50,662,663],{"class":52,"line":208},[50,664,82],{"emptyLinePlaceholder":81},[50,666,667,669,671,673,676],{"class":52,"line":234},[50,668,180],{"class":56},[50,670,184],{"class":183},[50,672,187],{"class":60},[50,674,675],{"class":183},"Struct",[50,677,193],{"class":60},[50,679,680,682,684],{"class":52,"line":266},[50,681,199],{"class":119},[50,683,202],{"class":60},[50,685,205],{"class":119},[50,687,688,690],{"class":52,"line":271},[50,689,211],{"class":60},[50,691,692],{"class":119},"int\n",[50,694,695,697],{"class":52,"line":276},[50,696,237],{"class":60},[50,698,205],{"class":119},[50,700,701],{"class":52,"line":301},[50,702,82],{"emptyLinePlaceholder":81},[50,704,705],{"class":52,"line":326},[50,706,82],{"emptyLinePlaceholder":81},[50,708,709,711,713,715],{"class":52,"line":345},[50,710,279],{"class":56},[50,712,282],{"class":56},[50,714,285],{"class":183},[50,716,440],{"class":60},[50,718,719,722,724,727,730,732,735],{"class":52,"line":354},[50,720,721],{"class":60},"    x_api_key: Annotated[",[50,723,240],{"class":119},[50,725,726],{"class":60},", Parameter(",[50,728,729],{"class":222},"header",[50,731,226],{"class":56},[50,733,734],{"class":129},"\"X-API-Key\"",[50,736,737],{"class":60},")],\n",[50,739,740,743,745],{"class":52,"line":359},[50,741,742],{"class":60},") -> ",[50,744,240],{"class":119},[50,746,298],{"class":60},[50,748,749,751,753,755,757,759,761,763],{"class":52,"line":364},[50,750,304],{"class":56},[50,752,307],{"class":56},[50,754,310],{"class":119},[50,756,313],{"class":56},[50,758,316],{"class":60},[50,760,319],{"class":56},[50,762,310],{"class":119},[50,764,298],{"class":60},[50,766,767,769,772,774],{"class":52,"line":393},[50,768,329],{"class":56},[50,770,771],{"class":60}," NotAuthorizedException(",[50,773,340],{"class":129},[50,775,139],{"class":60},[50,777,778,780],{"class":52,"line":398},[50,779,348],{"class":56},[50,781,351],{"class":60},[50,783,784],{"class":52,"line":403},[50,785,82],{"emptyLinePlaceholder":81},[50,787,788],{"class":52,"line":430},[50,789,82],{"emptyLinePlaceholder":81},[50,791,792,795,797,799,802,805,808,810],{"class":52,"line":443},[50,793,794],{"class":183},"@get",[50,796,187],{"class":60},[50,798,411],{"class":129},[50,800,801],{"class":119},"{report_id",[50,803,804],{"class":56},":",[50,806,807],{"class":119},"str}",[50,809,417],{"class":129},[50,811,139],{"class":60},[50,813,814,816,818,820,823,825,828,830],{"class":52,"line":454},[50,815,279],{"class":56},[50,817,282],{"class":56},[50,819,437],{"class":183},[50,821,822],{"class":60},"(report_id: ",[50,824,240],{"class":119},[50,826,827],{"class":60},", api_key: ",[50,829,240],{"class":119},[50,831,468],{"class":60},[50,833,834,836],{"class":52,"line":465},[50,835,348],{"class":56},[50,837,476],{"class":60},[50,839,840,842,844],{"class":52,"line":471},[50,841,482],{"class":222},[50,843,226],{"class":56},[50,845,487],{"class":60},[50,847,848,850,852,854],{"class":52,"line":479},[50,849,493],{"class":222},[50,851,226],{"class":56},[50,853,145],{"class":119},[50,855,451],{"class":60},[50,857,858,860,862,864,866,868,870],{"class":52,"line":490},[50,859,505],{"class":222},[50,861,226],{"class":56},[50,863,380],{"class":60},[50,865,512],{"class":129},[50,867,133],{"class":60},[50,869,517],{"class":129},[50,871,520],{"class":60},[50,873,874],{"class":52,"line":502},[50,875,526],{"class":60},[50,877,878],{"class":52,"line":523},[50,879,82],{"emptyLinePlaceholder":81},[50,881,883],{"class":52,"line":882},36,[50,884,82],{"emptyLinePlaceholder":81},[50,886,888,890,892],{"class":52,"line":887},37,[50,889,367],{"class":60},[50,891,226],{"class":56},[50,893,894],{"class":60}," Litestar(\n",[50,896,898,901,903],{"class":52,"line":897},38,[50,899,900],{"class":222},"    route_handlers",[50,902,226],{"class":56},[50,904,905],{"class":60},"[get_report],\n",[50,907,909,912,914,917,920],{"class":52,"line":908},39,[50,910,911],{"class":222},"    dependencies",[50,913,226],{"class":56},[50,915,916],{"class":60},"{",[50,918,919],{"class":129},"\"api_key\"",[50,921,922],{"class":60},": Provide(require_api_key)},\n",[50,924,926],{"class":52,"line":925},40,[50,927,139],{"class":60},[14,929,930,931,934],{},"Two details matter commercially. Litestar requires a return annotation on every handler and derives the response schema from it, so an untyped handler fails at startup instead of shipping an undocumented endpoint. And path parameters carry their type in the path itself (",[47,932,933],{},"{report_id:str}","), which catches a class of routing bug that FastAPI defers to request time. Neither is a headline feature, but both remove a category of production surprise.",[32,936,938],{"id":937},"dependency-injection-signature-graph-vs-layered-scopes","Dependency injection: signature graph vs layered scopes",[14,940,941,942,945,946,949,950,953],{},"FastAPI resolves dependencies from the handler signature. Every handler that needs a database session, a tenant, and an authenticated key repeats those three ",[47,943,944],{},"Depends"," markers. That is explicit and easy to read one function at a time, and it gets tedious across forty endpoints. Litestar puts dependencies in a ",[47,947,948],{},"dependencies"," mapping that exists at four levels — application, router, controller, and handler — where the innermost declaration wins. You register a session provider once on the app and every handler that names ",[47,951,952],{},"db_session"," as a parameter receives it.",[955,956,964,965,964,968,964,972,964,978,964,986,964,990,964,997,964,1006,964,1011,964,1015,964,1019,964,1022,964,1026,964,1029,964,1033,964,1038,964,1042,964,1045,964,1049,964,1052,964,1055,964,1058,964,1062,964,1065],"svg",{"viewBox":957,"role":958,"ariaLabelledBy":959,"xmlns":962,"style":963},"0 0 720 296","img",[960,961],"fvl-di-t","fvl-di-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;margin:1.5rem 0;font-family:var(--font-sans);","\n  ",[375,966,967],{"id":960},"Dependency declaration in FastAPI versus Litestar",[969,970,971],"desc",{"id":961},"FastAPI repeats every Depends marker in each handler signature, while Litestar declares dependencies once per scope from application down to handler, with inner scopes overriding outer ones.",[973,974],"rect",{"x":229,"y":229,"width":975,"height":976,"fill":977},"720","296","var(--c-surface)",[979,980,985],"text",{"x":981,"y":982,"fill":983,"style":984},"190","24","var(--c-text)","text-anchor:middle;font-size:14;font-family:var(--font-sans);","FastAPI: per-handler graph",[979,987,989],{"x":988,"y":982,"fill":983,"style":984},"530","Litestar: layered scopes",[52,991],{"x1":992,"y1":993,"x2":992,"y2":994,"stroke":995,"style":996},"360","36","272","var(--c-border)","stroke-width:1;",[973,998],{"x":999,"y":1000,"width":1001,"height":1000,"rx":1002,"fill":1003,"stroke":1004,"style":1005},"60","40","260","8","var(--c-surface-alt)","var(--c-blue)","stroke-width:2;",[979,1007,1010],{"x":981,"y":1008,"fill":983,"style":1009},"64","text-anchor:middle;font-size:12;font-family:var(--font-sans);","@app.get(\"\u002Freports\u002F{id}\")",[973,1012],{"x":999,"y":1013,"width":1001,"height":1000,"rx":1002,"fill":977,"stroke":1014,"style":1005},"92","var(--c-coral)",[979,1016,1018],{"x":981,"y":1017,"fill":983,"style":1009},"116","Depends(get_settings)",[973,1020],{"x":999,"y":1021,"width":1001,"height":1000,"rx":1002,"fill":977,"stroke":1014,"style":1005},"144",[979,1023,1025],{"x":981,"y":1024,"fill":983,"style":1009},"168","Depends(get_db_session)",[973,1027],{"x":999,"y":1028,"width":1001,"height":1000,"rx":1002,"fill":977,"stroke":1014,"style":1005},"196",[979,1030,1032],{"x":981,"y":1031,"fill":983,"style":1009},"220","Depends(require_api_key)",[979,1034,1037],{"x":981,"y":1035,"fill":1036,"style":1009},"262","var(--c-text-muted)","Repeated in every signature",[973,1039],{"x":1040,"y":1000,"width":1001,"height":1000,"rx":1002,"fill":1003,"stroke":1041,"style":1005},"400","var(--c-teal)",[979,1043,1044],{"x":988,"y":1008,"fill":983,"style":1009},"Litestar(dependencies=...)",[973,1046],{"x":1047,"y":1013,"width":1048,"height":1000,"rx":1002,"fill":977,"stroke":1041,"style":1005},"416","228",[979,1050,1051],{"x":988,"y":1017,"fill":983,"style":1009},"Router(dependencies=...)",[973,1053],{"x":1054,"y":1021,"width":1028,"height":1000,"rx":1002,"fill":977,"stroke":1041,"style":1005},"432",[979,1056,1057],{"x":988,"y":1024,"fill":983,"style":1009},"Controller scope",[973,1059],{"x":1060,"y":1028,"width":1061,"height":1000,"rx":1002,"fill":977,"stroke":1004,"style":1005},"448","164",[979,1063,1064],{"x":988,"y":1031,"fill":983,"style":1009},"@get(dependencies=)",[979,1066,1067],{"x":988,"y":1035,"fill":1036,"style":1009},"Inner scope overrides outer",[14,1069,1070,1071,1074,1075,1078,1079,1082],{},"The practical win is resource lifecycle. Litestar accepts async generator providers and closes them after the response is sent, and ",[47,1072,1073],{},"use_cache=True"," memoizes a provider for the duration of a single request so three handlers sharing a tenant lookup hit the database once. FastAPI's ",[47,1076,1077],{},"yield"," dependencies do the same cleanup, but caching is implicit and keyed on the callable, which surprises people who wrap providers in lambdas or ",[47,1080,1081],{},"functools.partial",".",[40,1084,1086],{"className":42,"code":1085,"language":44,"meta":45,"style":45},"import os\nfrom collections.abc import AsyncGenerator\n\nimport httpx\nfrom litestar import Litestar, Router, get\nfrom litestar.di import Provide\n\n\nasync def upstream_client() -> AsyncGenerator[httpx.AsyncClient, None]:\n    async with httpx.AsyncClient(\n        base_url=os.environ[\"UPSTREAM_BASE_URL\"],\n        timeout=float(os.getenv(\"UPSTREAM_TIMEOUT_SECONDS\", \"10\")),\n        limits=httpx.Limits(max_connections=int(os.getenv(\"UPSTREAM_MAX_CONNS\", \"50\"))),\n    ) as client:\n        yield client\n\n\nasync def tenant_id(x_tenant: str) -> str:\n    return x_tenant.strip().lower()\n\n\n@get(\"\u002Fusage\")\nasync def usage(client: httpx.AsyncClient, tenant_id: str) -> dict[str, str]:\n    response = await client.get(\"\u002Fusage\", params={\"tenant\": tenant_id})\n    response.raise_for_status()\n    return response.json()\n\n\nbilling = Router(\n    path=\"\u002Fbilling\",\n    route_handlers=[usage],\n    dependencies={\"tenant_id\": Provide(tenant_id, use_cache=True)},\n)\n\napp = Litestar(\n    route_handlers=[billing],\n    dependencies={\"client\": Provide(upstream_client)},\n)\n",[47,1087,1088,1094,1106,1110,1117,1128,1138,1142,1146,1164,1175,1191,1214,1243,1254,1262,1266,1270,1290,1297,1301,1305,1316,1341,1371,1376,1383,1387,1391,1401,1413,1422,1447,1451,1455,1463,1472,1486],{"__ignoreMap":45},[50,1089,1090,1092],{"class":52,"line":53},[50,1091,57],{"class":56},[50,1093,61],{"class":60},[50,1095,1096,1098,1101,1103],{"class":52,"line":64},[50,1097,67],{"class":56},[50,1099,1100],{"class":60}," collections.abc ",[50,1102,57],{"class":56},[50,1104,1105],{"class":60}," AsyncGenerator\n",[50,1107,1108],{"class":52,"line":78},[50,1109,82],{"emptyLinePlaceholder":81},[50,1111,1112,1114],{"class":52,"line":85},[50,1113,57],{"class":56},[50,1115,1116],{"class":60}," httpx\n",[50,1118,1119,1121,1123,1125],{"class":52,"line":98},[50,1120,67],{"class":56},[50,1122,565],{"class":60},[50,1124,57],{"class":56},[50,1126,1127],{"class":60}," Litestar, Router, get\n",[50,1129,1130,1132,1134,1136],{"class":52,"line":111},[50,1131,67],{"class":56},[50,1133,577],{"class":60},[50,1135,57],{"class":56},[50,1137,582],{"class":60},[50,1139,1140],{"class":52,"line":116},[50,1141,82],{"emptyLinePlaceholder":81},[50,1143,1144],{"class":52,"line":142},[50,1145,82],{"emptyLinePlaceholder":81},[50,1147,1148,1150,1152,1155,1158,1161],{"class":52,"line":167},[50,1149,279],{"class":56},[50,1151,282],{"class":56},[50,1153,1154],{"class":183}," upstream_client",[50,1156,1157],{"class":60},"() -> AsyncGenerator[httpx.AsyncClient, ",[50,1159,1160],{"class":119},"None",[50,1162,1163],{"class":60},"]:\n",[50,1165,1166,1169,1172],{"class":52,"line":172},[50,1167,1168],{"class":56},"    async",[50,1170,1171],{"class":56}," with",[50,1173,1174],{"class":60}," httpx.AsyncClient(\n",[50,1176,1177,1180,1182,1185,1188],{"class":52,"line":177},[50,1178,1179],{"class":222},"        base_url",[50,1181,226],{"class":56},[50,1183,1184],{"class":60},"os.environ[",[50,1186,1187],{"class":129},"\"UPSTREAM_BASE_URL\"",[50,1189,1190],{"class":60},"],\n",[50,1192,1193,1196,1198,1201,1203,1206,1208,1211],{"class":52,"line":196},[50,1194,1195],{"class":222},"        timeout",[50,1197,226],{"class":56},[50,1199,1200],{"class":119},"float",[50,1202,153],{"class":60},[50,1204,1205],{"class":129},"\"UPSTREAM_TIMEOUT_SECONDS\"",[50,1207,133],{"class":60},[50,1209,1210],{"class":129},"\"10\"",[50,1212,1213],{"class":60},")),\n",[50,1215,1216,1219,1221,1224,1227,1229,1231,1233,1236,1238,1240],{"class":52,"line":208},[50,1217,1218],{"class":222},"        limits",[50,1220,226],{"class":56},[50,1222,1223],{"class":60},"httpx.Limits(",[50,1225,1226],{"class":222},"max_connections",[50,1228,226],{"class":56},[50,1230,214],{"class":119},[50,1232,153],{"class":60},[50,1234,1235],{"class":129},"\"UPSTREAM_MAX_CONNS\"",[50,1237,133],{"class":60},[50,1239,161],{"class":129},[50,1241,1242],{"class":60},"))),\n",[50,1244,1245,1248,1251],{"class":52,"line":234},[50,1246,1247],{"class":60},"    ) ",[50,1249,1250],{"class":56},"as",[50,1252,1253],{"class":60}," client:\n",[50,1255,1256,1259],{"class":52,"line":266},[50,1257,1258],{"class":56},"        yield",[50,1260,1261],{"class":60}," client\n",[50,1263,1264],{"class":52,"line":271},[50,1265,82],{"emptyLinePlaceholder":81},[50,1267,1268],{"class":52,"line":276},[50,1269,82],{"emptyLinePlaceholder":81},[50,1271,1272,1274,1276,1279,1282,1284,1286,1288],{"class":52,"line":301},[50,1273,279],{"class":56},[50,1275,282],{"class":56},[50,1277,1278],{"class":183}," tenant_id",[50,1280,1281],{"class":60},"(x_tenant: ",[50,1283,240],{"class":119},[50,1285,742],{"class":60},[50,1287,240],{"class":119},[50,1289,298],{"class":60},[50,1291,1292,1294],{"class":52,"line":326},[50,1293,348],{"class":56},[50,1295,1296],{"class":60}," x_tenant.strip().lower()\n",[50,1298,1299],{"class":52,"line":345},[50,1300,82],{"emptyLinePlaceholder":81},[50,1302,1303],{"class":52,"line":354},[50,1304,82],{"emptyLinePlaceholder":81},[50,1306,1307,1309,1311,1314],{"class":52,"line":359},[50,1308,794],{"class":183},[50,1310,187],{"class":60},[50,1312,1313],{"class":129},"\"\u002Fusage\"",[50,1315,139],{"class":60},[50,1317,1318,1320,1322,1325,1328,1330,1333,1335,1337,1339],{"class":52,"line":364},[50,1319,279],{"class":56},[50,1321,282],{"class":56},[50,1323,1324],{"class":183}," usage",[50,1326,1327],{"class":60},"(client: httpx.AsyncClient, tenant_id: ",[50,1329,240],{"class":119},[50,1331,1332],{"class":60},") -> dict[",[50,1334,240],{"class":119},[50,1336,133],{"class":60},[50,1338,240],{"class":119},[50,1340,1163],{"class":60},[50,1342,1343,1346,1348,1351,1354,1356,1358,1361,1363,1365,1368],{"class":52,"line":393},[50,1344,1345],{"class":60},"    response ",[50,1347,226],{"class":56},[50,1349,1350],{"class":56}," await",[50,1352,1353],{"class":60}," client.get(",[50,1355,1313],{"class":129},[50,1357,133],{"class":60},[50,1359,1360],{"class":222},"params",[50,1362,226],{"class":56},[50,1364,916],{"class":60},[50,1366,1367],{"class":129},"\"tenant\"",[50,1369,1370],{"class":60},": tenant_id})\n",[50,1372,1373],{"class":52,"line":398},[50,1374,1375],{"class":60},"    response.raise_for_status()\n",[50,1377,1378,1380],{"class":52,"line":403},[50,1379,348],{"class":56},[50,1381,1382],{"class":60}," response.json()\n",[50,1384,1385],{"class":52,"line":430},[50,1386,82],{"emptyLinePlaceholder":81},[50,1388,1389],{"class":52,"line":443},[50,1390,82],{"emptyLinePlaceholder":81},[50,1392,1393,1396,1398],{"class":52,"line":454},[50,1394,1395],{"class":60},"billing ",[50,1397,226],{"class":56},[50,1399,1400],{"class":60}," Router(\n",[50,1402,1403,1406,1408,1411],{"class":52,"line":465},[50,1404,1405],{"class":222},"    path",[50,1407,226],{"class":56},[50,1409,1410],{"class":129},"\"\u002Fbilling\"",[50,1412,451],{"class":60},[50,1414,1415,1417,1419],{"class":52,"line":471},[50,1416,900],{"class":222},[50,1418,226],{"class":56},[50,1420,1421],{"class":60},"[usage],\n",[50,1423,1424,1426,1428,1430,1433,1436,1439,1441,1444],{"class":52,"line":479},[50,1425,911],{"class":222},[50,1427,226],{"class":56},[50,1429,916],{"class":60},[50,1431,1432],{"class":129},"\"tenant_id\"",[50,1434,1435],{"class":60},": Provide(tenant_id, ",[50,1437,1438],{"class":222},"use_cache",[50,1440,226],{"class":56},[50,1442,1443],{"class":119},"True",[50,1445,1446],{"class":60},")},\n",[50,1448,1449],{"class":52,"line":490},[50,1450,139],{"class":60},[50,1452,1453],{"class":52,"line":502},[50,1454,82],{"emptyLinePlaceholder":81},[50,1456,1457,1459,1461],{"class":52,"line":523},[50,1458,367],{"class":60},[50,1460,226],{"class":56},[50,1462,894],{"class":60},[50,1464,1465,1467,1469],{"class":52,"line":882},[50,1466,900],{"class":222},[50,1468,226],{"class":56},[50,1470,1471],{"class":60},"[billing],\n",[50,1473,1474,1476,1478,1480,1483],{"class":52,"line":887},[50,1475,911],{"class":222},[50,1477,226],{"class":56},[50,1479,916],{"class":60},[50,1481,1482],{"class":129},"\"client\"",[50,1484,1485],{"class":60},": Provide(upstream_client)},\n",[50,1487,1488],{"class":52,"line":897},[50,1489,139],{"class":60},[14,1491,1492,1493,1496,1497,1499,1500,1503],{},"One pooled ",[47,1494,1495],{},"httpx.AsyncClient"," for the whole application, declared once, torn down on shutdown. Doing the equivalent in FastAPI means a lifespan context manager plus a ",[47,1498,944],{}," shim that reads it off ",[47,1501,1502],{},"app.state"," — perhaps eight extra lines, but eight lines every team writes slightly differently.",[32,1505,1507],{"id":1506},"validation-throughput-where-the-cpu-actually-goes","Validation throughput: where the CPU actually goes",[14,1509,1510],{},"Litestar defaults to msgspec for decoding request bodies and encoding responses. msgspec parses JSON directly into typed structs in C, skipping the intermediate Python dictionary that Pydantic v2 still constructs before validating. On a small JSON round trip, that difference dominates the request budget, because at these payload sizes serialization is 40 to 60 percent of the CPU time your worker spends.",[955,1512,964,1517,964,1520,964,1523,964,1526,964,1530,964,1535,964,1541,964,1547,964,1552,964,1556,964,1560,964,1564,964,1568,964,1572,964,1576],{"viewBox":1513,"role":958,"ariaLabelledBy":1514,"xmlns":962,"style":963},"0 0 720 236",[1515,1516],"fvl-perf-t","fvl-perf-d",[375,1518,1519],{"id":1515},"Requests per second by framework and validation layer",[969,1521,1522],{"id":1516},"A single worker on four vCPUs serves roughly 9,400 requests per second with FastAPI and Pydantic, 11,200 with Litestar and Pydantic, and 17,800 with Litestar and msgspec structs.",[973,1524],{"x":229,"y":229,"width":975,"height":1525,"fill":977},"236",[979,1527,1529],{"x":992,"y":1528,"fill":983,"style":984},"26","POST round trip, 2 KB body, one worker",[52,1531],{"x1":1532,"y1":1533,"x2":1532,"y2":1534,"stroke":995,"style":996},"250","44","192",[979,1536,1540],{"x":1537,"y":1538,"fill":983,"style":1539},"240","72","text-anchor:end;font-size:12;font-family:var(--font-sans);","FastAPI + Pydantic",[973,1542],{"x":1532,"y":1543,"width":1544,"height":1545,"rx":1546,"fill":1003,"stroke":1014,"style":1005},"52","209","28","4",[979,1548,1551],{"x":1549,"y":1538,"fill":983,"style":1550},"469","text-anchor:start;font-size:12;font-family:var(--font-sans);","9,400",[979,1553,1555],{"x":1537,"y":1554,"fill":983,"style":1539},"122","Litestar + Pydantic",[973,1557],{"x":1532,"y":1558,"width":1559,"height":1545,"rx":1546,"fill":1003,"stroke":1004,"style":1005},"102","249",[979,1561,1563],{"x":1562,"y":1554,"fill":983,"style":1550},"509","11,200",[979,1565,1567],{"x":1537,"y":1566,"fill":983,"style":1539},"172","Litestar + msgspec",[973,1569],{"x":1532,"y":1570,"width":1571,"height":1545,"rx":1546,"fill":1003,"stroke":1041,"style":1005},"152","396",[979,1573,1575],{"x":1574,"y":1566,"fill":983,"style":1550},"654","17,800",[979,1577,1579],{"x":992,"y":1578,"fill":1036,"style":1009},"216","Ratios hold; absolutes move with your hardware",[14,1581,1582,1583,1082],{},"Read that chart as a ratio, not a promise. On a four-vCPU box with a trivial handler body, Litestar with msgspec serves roughly 1.9 times the throughput of FastAPI with Pydantic. The moment your handler awaits Postgres for 8 ms or an upstream vendor for 120 ms, that advantage shrinks toward nothing, because the framework is no longer the bottleneck. Work out your own split before believing any benchmark: measure the median handler duration, subtract the I\u002FO wait, and see how much of the remainder is validation. If framework overhead is under 15 percent of your request time, a rewrite buys you a rounding error on a bill you can compute with ",[18,1584,1586],{"href":1585},"\u002Fbuilding-monetizing-api-driven-micro-saas\u002Fdesigning-api-pricing-tiers\u002Fcalculating-cost-per-api-request\u002F","cost per API request",[14,1588,1589,1590,1594],{},"Litestar does not force msgspec on you. It ships a Pydantic plugin, and Pydantic models work as handler parameters and return types with no extra wiring, which is what the middle bar measures. That matters if you already own a library of validators built on ",[18,1591,1593],{"href":1592},"\u002Fgetting-started-with-python-apis-for-builders\u002Fparsing-json-responses\u002Fvalidating-json-with-pydantic-v2\u002F","Pydantic v2 JSON validation"," — you keep them and still gain the routing and DI improvements. You also give up most of the throughput advantage, which is the honest trade.",[32,1596,1598],{"id":1597},"plugins-docs-and-the-hiring-tax","Plugins, docs, and the hiring tax",[14,1600,1601,1602,1606],{},"FastAPI is a thin, unopinionated core plus an enormous ecosystem. Litestar inverts that: more of the stack is in the box, less of it is on PyPI. The first-party SQLAlchemy plugin wires session management, transaction scoping, and repository helpers in about ten lines, which is a genuine head start compared with assembling the same thing yourself around ",[18,1603,1605],{"href":1604},"\u002Fscaling-and-operating-production-python-apis\u002Fasync-database-access-with-sqlalchemy\u002F","async SQLAlchemy database access",". Litestar also includes channels for server-sent events and WebSocket pub\u002Fsub, response caching keyed on request attributes, and a structured logging config — all things you bolt on separately in FastAPI.",[955,1608,964,1613,964,1616,964,1619,964,1622,964,1627,964,1631,964,1636,964,1640,964,1644,964,1646,964,1649,964,1652,964,1654,964,1658,964,1661,964,1663,964,1666,964,1668,964,1670,964,1673,964,1676,964,1678,964,1682,964,1684,964,1686,964,1690,964,1694,964,1697,964,1701,964,1704],{"viewBox":1609,"role":958,"ariaLabelledBy":1610,"xmlns":962,"style":963},"0 0 720 320",[1611,1612],"fvl-eco-t","fvl-eco-d",[375,1614,1615],{"id":1611},"What each framework ships in its core package",[969,1617,1618],{"id":1612},"Litestar includes an ORM plugin, session and auth backends, and response caching in core, while FastAPI leads decisively on community answers and hiring pool depth.",[973,1620],{"x":229,"y":229,"width":975,"height":1621,"fill":977},"320",[979,1623,1626],{"x":1624,"y":1625,"fill":983,"style":984},"440","34","FastAPI",[979,1628,1630],{"x":1629,"y":1625,"fill":983,"style":984},"612","Litestar",[52,1632],{"x1":1633,"y1":1634,"x2":1635,"y2":1634,"stroke":995,"style":996},"30","46","690",[979,1637,1639],{"x":1633,"y":1638,"fill":983,"style":1550},"76","OpenAPI schema generation",[1641,1642],"circle",{"cx":1624,"cy":1538,"r":1643,"fill":1041},"9",[1641,1645],{"cx":1629,"cy":1538,"r":1643,"fill":1041},[979,1647,1648],{"x":1633,"y":1017,"fill":983,"style":1550},"First-party ORM plugin",[1641,1650],{"cx":1624,"cy":1651,"r":1643,"fill":977,"stroke":995,"style":1005},"112",[1641,1653],{"cx":1629,"cy":1651,"r":1643,"fill":1041},[979,1655,1657],{"x":1633,"y":1656,"fill":983,"style":1550},"156","Auth and session backends",[1641,1659],{"cx":1624,"cy":1570,"r":1643,"fill":1660},"var(--c-yellow)",[1641,1662],{"cx":1629,"cy":1570,"r":1643,"fill":1041},[979,1664,1665],{"x":1633,"y":1028,"fill":983,"style":1550},"Response caching in core",[1641,1667],{"cx":1624,"cy":1534,"r":1643,"fill":977,"stroke":995,"style":1005},[1641,1669],{"cx":1629,"cy":1534,"r":1643,"fill":1041},[979,1671,1672],{"x":1633,"y":1525,"fill":983,"style":1550},"Answered questions online",[1641,1674],{"cx":1624,"cy":1675,"r":1643,"fill":1041},"232",[1641,1677],{"cx":1629,"cy":1675,"r":1643,"fill":1660},[979,1679,1681],{"x":1633,"y":1680,"fill":983,"style":1550},"276","Contractors who know it",[1641,1683],{"cx":1624,"cy":994,"r":1643,"fill":1041},[1641,1685],{"cx":1629,"cy":994,"r":1643,"fill":977,"stroke":995,"style":1005},[1641,1687],{"cx":1634,"cy":1688,"r":1689,"fill":1041},"304","7",[979,1691,1693],{"x":999,"y":1692,"fill":1036,"style":1550},"308","strong",[1641,1695],{"cx":1696,"cy":1688,"r":1689,"fill":1660},"146",[979,1698,1700],{"x":1699,"y":1692,"fill":1036,"style":1550},"160","partial or add-on",[1641,1702],{"cx":1703,"cy":1688,"r":1689,"fill":977,"stroke":995,"style":1005},"316",[979,1705,1707],{"x":1706,"y":1692,"fill":1036,"style":1550},"330","thin or absent",[14,1709,1710],{},"Now the uncomfortable column. Litestar's own documentation is excellent — better organized than FastAPI's, with a real API reference rather than a tutorial that trails off. What it lacks is the deep back catalogue: the Stack Overflow answer from 2023 that matches your exact traceback, the blog post about running it on your specific host, the model weights of every coding assistant trained on millions of FastAPI snippets. Ask an assistant to scaffold a Litestar controller and it will confidently hand you FastAPI syntax with the imports renamed. Budget real hours for that, and more if you plan to hand the codebase to a contractor.",[1712,1713,1714,1728],"table",{},[1715,1716,1717],"thead",{},[1718,1719,1720,1724,1726],"tr",{},[1721,1722,1723],"th",{},"Dimension",[1721,1725,1626],{},[1721,1727,1630],{},[1729,1730,1731,1743,1754,1765],"tbody",{},[1718,1732,1733,1737,1740],{},[1734,1735,1736],"td",{},"Dependency scoping",[1734,1738,1739],{},"Per signature",[1734,1741,1742],{},"Four nested scopes",[1718,1744,1745,1748,1751],{},[1734,1746,1747],{},"Default codec",[1734,1749,1750],{},"Pydantic v2",[1734,1752,1753],{},"msgspec",[1718,1755,1756,1759,1762],{},[1734,1757,1758],{},"ORM integration",[1734,1760,1761],{},"Community",[1734,1763,1764],{},"First party",[1718,1766,1767,1770,1773],{},[1734,1768,1769],{},"Answers and hiring pool",[1734,1771,1772],{},"Very deep",[1734,1774,1775],{},"Narrow",[32,1777,1779],{"id":1778},"when-to-choose-each","When to choose each",[14,1781,1782],{},"Pick Litestar for a greenfield service when three things are true at once: the workload is serialization-heavy rather than I\u002FO-bound, you own the codebase for the next two years, and you want the ORM, auth, and caching decisions made for you. Data-shaping endpoints, telemetry ingestion, and internal aggregation services all fit that profile. So does anything where the response body is large and the database work is a single indexed read.",[14,1784,1785,1786,1082],{},"Stay on FastAPI when the endpoint spends most of its time waiting — on Postgres, on Stripe, on a model provider. Stay when you plan to hire contractors, when you lean on assistants for scaffolding, or when a niche integration you need already exists as a FastAPI extension. And stay when you are pre-revenue: the framework is never the reason a first commercial API fails, and the same reasoning applies to the ",[18,1787,1789],{"href":1788},"\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-flask-for-api-development\u002F","FastAPI versus Flask decision",[955,1791,964,1796,964,1799,964,1802,964,1805,964,1809,964,1813,964,1817,964,1821,964,1824,964,1826,964,1830,964,1834,964,1837,964,1840,964,1842,964,1845,964,1848,964,1850,964,1852,964,1856,964,1858,964,1860,964,1864,964,1868,964,1871,964,1873,964,1876,964,1879,964,1881,964,1884,964,1887,964,1890,964,1893,964,1896],{"viewBox":1792,"role":958,"ariaLabelledBy":1793,"xmlns":962,"style":963},"0 0 720 312",[1794,1795],"fvl-tree-t","fvl-tree-d",[375,1797,1798],{"id":1794},"Decision tree for choosing between FastAPI and Litestar",[969,1800,1801],{"id":1795},"Greenfield teams fluent in FastAPI should stay; performance-first builds should pick Litestar. Existing production services should only port when serialization dominates the profile.",[973,1803],{"x":229,"y":229,"width":975,"height":1804,"fill":977},"312",[973,1806],{"x":1807,"y":982,"width":1808,"height":1533,"rx":1002,"fill":1003,"stroke":1004,"style":1005},"270","180",[979,1810,1812],{"x":992,"y":1811,"fill":983,"style":1009},"51","Where are you now?",[52,1814],{"x1":992,"y1":1815,"x2":992,"y2":1816,"stroke":995,"style":1005},"68","88",[52,1818],{"x1":1819,"y1":1816,"x2":1820,"y2":1816,"stroke":995,"style":1005},"170","550",[52,1822],{"x1":1819,"y1":1816,"x2":1819,"y2":1823,"stroke":995,"style":1005},"108",[52,1825],{"x1":1820,"y1":1816,"x2":1820,"y2":1823,"stroke":995,"style":1005},[973,1827],{"x":1828,"y":1823,"width":1829,"height":1533,"rx":1002,"fill":977,"stroke":995,"style":1005},"70","200",[979,1831,1833],{"x":1819,"y":1832,"fill":983,"style":1009},"135","Greenfield service",[973,1835],{"x":1836,"y":1823,"width":1829,"height":1533,"rx":1002,"fill":977,"stroke":995,"style":1005},"450",[979,1838,1839],{"x":1820,"y":1832,"fill":983,"style":1009},"FastAPI in production",[52,1841],{"x1":1819,"y1":1570,"x2":1819,"y2":1024,"stroke":995,"style":1005},[52,1843],{"x1":1844,"y1":1024,"x2":1532,"y2":1024,"stroke":995,"style":1005},"90",[52,1846],{"x1":1844,"y1":1024,"x2":1844,"y2":1847,"stroke":995,"style":1005},"188",[52,1849],{"x1":1532,"y1":1024,"x2":1532,"y2":1847,"stroke":995,"style":1005},[52,1851],{"x1":1820,"y1":1570,"x2":1820,"y2":1024,"stroke":995,"style":1005},[52,1853],{"x1":1854,"y1":1024,"x2":1855,"y2":1024,"stroke":995,"style":1005},"470","630",[52,1857],{"x1":1854,"y1":1024,"x2":1854,"y2":1847,"stroke":995,"style":1005},[52,1859],{"x1":1855,"y1":1024,"x2":1855,"y2":1847,"stroke":995,"style":1005},[973,1861],{"x":1862,"y":1847,"width":1863,"height":1008,"rx":1002,"fill":977,"stroke":1014,"style":1005},"20","140",[979,1865,1867],{"x":1844,"y":1866,"fill":983,"style":1009},"214","Team fluent in",[979,1869,1870],{"x":1844,"y":1675,"fill":983,"style":1009},"FastAPI: stay",[973,1872],{"x":1808,"y":1847,"width":1863,"height":1008,"rx":1002,"fill":977,"stroke":1041,"style":1005},[979,1874,1875],{"x":1532,"y":1866,"fill":983,"style":1009},"Perf-first build",[979,1877,1878],{"x":1532,"y":1675,"fill":983,"style":1009},"go Litestar",[973,1880],{"x":1040,"y":1847,"width":1863,"height":1008,"rx":1002,"fill":977,"stroke":1014,"style":1005},[979,1882,1883],{"x":1854,"y":1866,"fill":983,"style":1009},"CPU bill is fine",[979,1885,1886],{"x":1854,"y":1675,"fill":983,"style":1009},"stay put",[973,1888],{"x":1889,"y":1847,"width":1863,"height":1008,"rx":1002,"fill":977,"stroke":1041,"style":1005},"560",[979,1891,1892],{"x":1855,"y":1866,"fill":983,"style":1009},"Codec tops the",[979,1894,1895],{"x":1855,"y":1675,"fill":983,"style":1009},"profile: port",[979,1897,1899],{"x":992,"y":1898,"fill":1036,"style":1009},"286","Rewrite only when the framework is the bottleneck",[14,1901,1902],{},"The cost model decides it. At 20 million requests a month with a 6 ms handler, framework overhead of 0.11 ms versus 0.06 ms is about 17 minutes of CPU per month — call it three cents. At 20 million requests against a 0.4 ms in-memory lookup, the same difference is roughly a third of your compute bill, and now the port pays for itself in a quarter.",[32,1904,1906],{"id":1905},"migration-path-mount-then-move","Migration path: mount, then move",[14,1908,1909,1910,1914],{},"Do not rewrite the service. Both frameworks are ASGI apps, so a Litestar app mounts inside a running FastAPI app under a path prefix and you migrate one route group at a time behind the same process, the same port, and the same deploy. This pairs naturally with the approach in ",[18,1911,1913],{"href":1912},"\u002Fscaling-and-operating-production-python-apis\u002Fversioning-and-evolving-public-apis\u002Furl-versioning-vs-header-versioning\u002F","URL versioning versus header versioning"," if you are already introducing a new prefix.",[40,1916,1918],{"className":42,"code":1917,"language":44,"meta":45,"style":45},"import os\n\nfrom fastapi import FastAPI\nfrom litestar import Litestar, get\n\n\n@get(\"\u002Fhealth\")\nasync def health() -> dict[str, str]:\n    return {\"status\": \"ok\", \"engine\": \"litestar\"}\n\n\nnext_gen = Litestar(route_handlers=[health])\n\napp = FastAPI(title=os.getenv(\"SERVICE_NAME\", \"reports\"))\napp.mount(os.getenv(\"NEXT_GEN_PREFIX\", \"\u002Fv2\"), next_gen)\n",[47,1919,1920,1926,1930,1941,1951,1955,1959,1970,1990,2018,2022,2026,2044,2048,2070],{"__ignoreMap":45},[50,1921,1922,1924],{"class":52,"line":53},[50,1923,57],{"class":56},[50,1925,61],{"class":60},[50,1927,1928],{"class":52,"line":64},[50,1929,82],{"emptyLinePlaceholder":81},[50,1931,1932,1934,1936,1938],{"class":52,"line":78},[50,1933,67],{"class":56},[50,1935,90],{"class":60},[50,1937,57],{"class":56},[50,1939,1940],{"class":60}," FastAPI\n",[50,1942,1943,1945,1947,1949],{"class":52,"line":85},[50,1944,67],{"class":56},[50,1946,565],{"class":60},[50,1948,57],{"class":56},[50,1950,570],{"class":60},[50,1952,1953],{"class":52,"line":98},[50,1954,82],{"emptyLinePlaceholder":81},[50,1956,1957],{"class":52,"line":111},[50,1958,82],{"emptyLinePlaceholder":81},[50,1960,1961,1963,1965,1968],{"class":52,"line":116},[50,1962,794],{"class":183},[50,1964,187],{"class":60},[50,1966,1967],{"class":129},"\"\u002Fhealth\"",[50,1969,139],{"class":60},[50,1971,1972,1974,1976,1979,1982,1984,1986,1988],{"class":52,"line":142},[50,1973,279],{"class":56},[50,1975,282],{"class":56},[50,1977,1978],{"class":183}," health",[50,1980,1981],{"class":60},"() -> dict[",[50,1983,240],{"class":119},[50,1985,133],{"class":60},[50,1987,240],{"class":119},[50,1989,1163],{"class":60},[50,1991,1992,1994,1997,2000,2002,2005,2007,2010,2012,2015],{"class":52,"line":167},[50,1993,348],{"class":56},[50,1995,1996],{"class":60}," {",[50,1998,1999],{"class":129},"\"status\"",[50,2001,202],{"class":60},[50,2003,2004],{"class":129},"\"ok\"",[50,2006,133],{"class":60},[50,2008,2009],{"class":129},"\"engine\"",[50,2011,202],{"class":60},[50,2013,2014],{"class":129},"\"litestar\"",[50,2016,2017],{"class":60},"}\n",[50,2019,2020],{"class":52,"line":172},[50,2021,82],{"emptyLinePlaceholder":81},[50,2023,2024],{"class":52,"line":177},[50,2025,82],{"emptyLinePlaceholder":81},[50,2027,2028,2031,2033,2036,2039,2041],{"class":52,"line":196},[50,2029,2030],{"class":60},"next_gen ",[50,2032,226],{"class":56},[50,2034,2035],{"class":60}," Litestar(",[50,2037,2038],{"class":222},"route_handlers",[50,2040,226],{"class":56},[50,2042,2043],{"class":60},"[health])\n",[50,2045,2046],{"class":52,"line":208},[50,2047,82],{"emptyLinePlaceholder":81},[50,2049,2050,2052,2054,2056,2058,2060,2062,2064,2066,2068],{"class":52,"line":234},[50,2051,367],{"class":60},[50,2053,226],{"class":56},[50,2055,372],{"class":60},[50,2057,375],{"class":222},[50,2059,226],{"class":56},[50,2061,380],{"class":60},[50,2063,383],{"class":129},[50,2065,133],{"class":60},[50,2067,388],{"class":129},[50,2069,164],{"class":60},[50,2071,2072,2075,2078,2080,2083],{"class":52,"line":266},[50,2073,2074],{"class":60},"app.mount(os.getenv(",[50,2076,2077],{"class":129},"\"NEXT_GEN_PREFIX\"",[50,2079,133],{"class":60},[50,2081,2082],{"class":129},"\"\u002Fv2\"",[50,2084,2085],{"class":60},"), next_gen)\n",[14,2087,2088,2089,2091,2092,2094,2095,2097,2098,2101,2102,2105,2106,2110],{},"Four concrete steps after the mount is live. First, port your leaf dependencies — the ones with no sub-dependencies — into a ",[47,2090,948],{}," mapping on the Litestar app, since these are pure functions and translate mechanically. Second, move one low-traffic route group and run both versions against the same request corpus, diffing response bodies byte for byte; msgspec omits nothing Pydantic emits, but field ordering and ",[47,2093,1160],{}," handling can drift. Third, convert response models: leave them as Pydantic while you build confidence, then switch the hot ones to ",[47,2096,532],{}," and re-measure. Fourth, port your test suite, replacing the FastAPI ",[47,2099,2100],{},"TestClient"," with ",[47,2103,2104],{},"litestar.testing.AsyncTestClient"," — the httpx-based patterns from ",[18,2107,2109],{"href":2108},"\u002Fscaling-and-operating-production-python-apis\u002Ftesting-python-apis-with-pytest\u002Ftesting-async-fastapi-endpoints-with-httpx\u002F","testing async endpoints with httpx"," transfer almost unchanged.",[14,2112,2113],{},"Before you start, check what your project actually pins. A ten-line audit tells you how deep the coupling runs.",[40,2115,2117],{"className":42,"code":2116,"language":44,"meta":45,"style":45},"import os\nimport tomllib\nfrom pathlib import Path\n\nPYPROJECT = Path(os.getenv(\"PYPROJECT_PATH\", \"pyproject.toml\"))\nCOUPLED = (\"fastapi-users\", \"fastapi-cache\", \"fastapi-limiter\", \"sqladmin\")\n\ndata = tomllib.loads(PYPROJECT.read_text(encoding=\"utf-8\"))\ndeps = data.get(\"project\", {}).get(\"dependencies\", [])\n\nfor raw in deps:\n    name = raw.split(\"[\")[0].split(\"=\")[0].split(\">\")[0].split(\"\u003C\")[0].strip()\n    match name:\n        case \"fastapi\":\n            print(f\"{name}: core framework, port route by route\")\n        case n if n in COUPLED:\n            print(f\"{n}: FastAPI-coupled, needs a Litestar replacement\")\n        case _:\n            print(f\"{name}: framework agnostic, carries over\")\n",[47,2118,2119,2125,2132,2144,2148,2168,2198,2202,2227,2249,2253,2267,2316,2324,2334,2359,2378,2400,2407],{"__ignoreMap":45},[50,2120,2121,2123],{"class":52,"line":53},[50,2122,57],{"class":56},[50,2124,61],{"class":60},[50,2126,2127,2129],{"class":52,"line":64},[50,2128,57],{"class":56},[50,2130,2131],{"class":60}," tomllib\n",[50,2133,2134,2136,2139,2141],{"class":52,"line":78},[50,2135,67],{"class":56},[50,2137,2138],{"class":60}," pathlib ",[50,2140,57],{"class":56},[50,2142,2143],{"class":60}," Path\n",[50,2145,2146],{"class":52,"line":85},[50,2147,82],{"emptyLinePlaceholder":81},[50,2149,2150,2153,2155,2158,2161,2163,2166],{"class":52,"line":98},[50,2151,2152],{"class":119},"PYPROJECT",[50,2154,123],{"class":56},[50,2156,2157],{"class":60}," Path(os.getenv(",[50,2159,2160],{"class":129},"\"PYPROJECT_PATH\"",[50,2162,133],{"class":60},[50,2164,2165],{"class":129},"\"pyproject.toml\"",[50,2167,164],{"class":60},[50,2169,2170,2173,2175,2178,2181,2183,2186,2188,2191,2193,2196],{"class":52,"line":111},[50,2171,2172],{"class":119},"COUPLED",[50,2174,123],{"class":56},[50,2176,2177],{"class":60}," (",[50,2179,2180],{"class":129},"\"fastapi-users\"",[50,2182,133],{"class":60},[50,2184,2185],{"class":129},"\"fastapi-cache\"",[50,2187,133],{"class":60},[50,2189,2190],{"class":129},"\"fastapi-limiter\"",[50,2192,133],{"class":60},[50,2194,2195],{"class":129},"\"sqladmin\"",[50,2197,139],{"class":60},[50,2199,2200],{"class":52,"line":116},[50,2201,82],{"emptyLinePlaceholder":81},[50,2203,2204,2207,2209,2212,2214,2217,2220,2222,2225],{"class":52,"line":142},[50,2205,2206],{"class":60},"data ",[50,2208,226],{"class":56},[50,2210,2211],{"class":60}," tomllib.loads(",[50,2213,2152],{"class":119},[50,2215,2216],{"class":60},".read_text(",[50,2218,2219],{"class":222},"encoding",[50,2221,226],{"class":56},[50,2223,2224],{"class":129},"\"utf-8\"",[50,2226,164],{"class":60},[50,2228,2229,2232,2234,2237,2240,2243,2246],{"class":52,"line":167},[50,2230,2231],{"class":60},"deps ",[50,2233,226],{"class":56},[50,2235,2236],{"class":60}," data.get(",[50,2238,2239],{"class":129},"\"project\"",[50,2241,2242],{"class":60},", {}).get(",[50,2244,2245],{"class":129},"\"dependencies\"",[50,2247,2248],{"class":60},", [])\n",[50,2250,2251],{"class":52,"line":172},[50,2252,82],{"emptyLinePlaceholder":81},[50,2254,2255,2258,2261,2264],{"class":52,"line":177},[50,2256,2257],{"class":56},"for",[50,2259,2260],{"class":60}," raw ",[50,2262,2263],{"class":56},"in",[50,2265,2266],{"class":60}," deps:\n",[50,2268,2269,2272,2274,2277,2280,2283,2285,2288,2291,2293,2295,2297,2300,2302,2304,2306,2309,2311,2313],{"class":52,"line":196},[50,2270,2271],{"class":60},"    name ",[50,2273,226],{"class":56},[50,2275,2276],{"class":60}," raw.split(",[50,2278,2279],{"class":129},"\"[\"",[50,2281,2282],{"class":60},")[",[50,2284,229],{"class":119},[50,2286,2287],{"class":60},"].split(",[50,2289,2290],{"class":129},"\"=\"",[50,2292,2282],{"class":60},[50,2294,229],{"class":119},[50,2296,2287],{"class":60},[50,2298,2299],{"class":129},"\">\"",[50,2301,2282],{"class":60},[50,2303,229],{"class":119},[50,2305,2287],{"class":60},[50,2307,2308],{"class":129},"\"\u003C\"",[50,2310,2282],{"class":60},[50,2312,229],{"class":119},[50,2314,2315],{"class":60},"].strip()\n",[50,2317,2318,2321],{"class":52,"line":208},[50,2319,2320],{"class":56},"    match",[50,2322,2323],{"class":60}," name:\n",[50,2325,2326,2329,2332],{"class":52,"line":234},[50,2327,2328],{"class":56},"        case",[50,2330,2331],{"class":129}," \"fastapi\"",[50,2333,298],{"class":60},[50,2335,2336,2339,2341,2344,2346,2348,2351,2354,2357],{"class":52,"line":266},[50,2337,2338],{"class":119},"            print",[50,2340,187],{"class":60},[50,2342,2343],{"class":56},"f",[50,2345,417],{"class":129},[50,2347,916],{"class":119},[50,2349,2350],{"class":60},"name",[50,2352,2353],{"class":119},"}",[50,2355,2356],{"class":129},": core framework, port route by route\"",[50,2358,139],{"class":60},[50,2360,2361,2363,2366,2369,2371,2373,2376],{"class":52,"line":271},[50,2362,2328],{"class":56},[50,2364,2365],{"class":60}," n ",[50,2367,2368],{"class":56},"if",[50,2370,2365],{"class":60},[50,2372,2263],{"class":56},[50,2374,2375],{"class":119}," COUPLED",[50,2377,298],{"class":60},[50,2379,2380,2382,2384,2386,2388,2390,2393,2395,2398],{"class":52,"line":276},[50,2381,2338],{"class":119},[50,2383,187],{"class":60},[50,2385,2343],{"class":56},[50,2387,417],{"class":129},[50,2389,916],{"class":119},[50,2391,2392],{"class":60},"n",[50,2394,2353],{"class":119},[50,2396,2397],{"class":129},": FastAPI-coupled, needs a Litestar replacement\"",[50,2399,139],{"class":60},[50,2401,2402,2404],{"class":52,"line":301},[50,2403,2328],{"class":56},[50,2405,2406],{"class":60}," _:\n",[50,2408,2409,2411,2413,2415,2417,2419,2421,2423,2426],{"class":52,"line":326},[50,2410,2338],{"class":119},[50,2412,187],{"class":60},[50,2414,2343],{"class":56},[50,2416,417],{"class":129},[50,2418,916],{"class":119},[50,2420,2350],{"class":60},[50,2422,2353],{"class":119},[50,2424,2425],{"class":129},": framework agnostic, carries over\"",[50,2427,139],{"class":60},[14,2429,2430],{},"Anything printed as FastAPI-coupled is the real migration cost. If that list is empty, a port is a weekend. If it contains an admin panel or an auth suite, it is a month, and the throughput gain almost never justifies a month.",[32,2432,2434],{"id":2433},"builder-verdict","Builder verdict",[14,2436,2437,2438,2442],{},"FastAPI wins for the overwhelming majority of commercial Python APIs, and it is not close on the axis that decides revenue. Your first two paying products will bottleneck on Postgres, on a payment provider, or on a model API — never on Pydantic. FastAPI's ecosystem depth means the auth library, the rate limiter, the observability integration, and the Stack Overflow answer all already exist, and every hour you do not spend inventing them is an hour spent on the feature a customer will pay for. Litestar earns the swap in exactly one situation: a serialization-dominated service you profiled, where the codec sits at the top of the flame graph and the msgspec path cuts your instance count. That is a real and satisfying win when it happens, and it is a specific engineering decision made after measurement — not a default. Ship on FastAPI, document it well via ",[18,2439,2441],{"href":2440},"\u002Fgetting-started-with-python-apis-for-builders\u002Fdocumenting-apis-with-openapi\u002F","OpenAPI",", and revisit Litestar when your own profiler tells you to.",[32,2444,2446],{"id":2445},"faq","FAQ",[14,2448,2449,2452],{},[1693,2450,2451],{},"Will switching to Litestar actually reduce my monthly hosting bill?","\nOnly if serialization dominates your profile. At 20 million requests a month with a 6 ms database-bound handler, the framework difference is worth a few cents. On a 0.4 ms in-memory lookup at the same volume, msgspec can cut instance count by a third, which is real money. Profile before you port.",[14,2454,2455,2458],{},[1693,2456,2457],{},"How risky is a mid-flight migration for paying customers?","\nLow if you mount the Litestar app under a path prefix inside the existing FastAPI app and move one route group at a time. Same process, same deploy, instant rollback by unmounting. The risk concentrates in response-body drift, so diff both implementations against the same request corpus before cutting traffic over.",[14,2460,2461,2464],{},[1693,2462,2463],{},"Can I keep my Pydantic models and validators on Litestar?","\nYes. Litestar's Pydantic plugin accepts models as handler parameters and return types with no extra wiring, so a validator library carries over intact. You keep the routing and dependency injection improvements but give up most of the throughput advantage, since Pydantic remains the codec.",[14,2466,2467,2470],{},[1693,2468,2469],{},"Does hiring get harder if I build on Litestar?","\nMeasurably. The contractor pool that knows Litestar is a fraction of the FastAPI pool, and coding assistants routinely emit FastAPI syntax when asked for Litestar. Budget extra onboarding hours per engineer, and treat that as a recurring cost against whatever compute you save.",[14,2472,2473,2476],{},[1693,2474,2475],{},"Which framework handles OpenAPI documentation better for a public API?","\nLitestar edges it. Return-type annotations are mandatory, so an undocumented endpoint fails at startup rather than shipping silently, and the schema includes examples derived from your types. FastAPI's output is perfectly good but lets you ship handlers with no declared response model.",[32,2478,2480],{"id":2479},"related","Related",[14,2482,2483],{},[1693,2484,2485],{},"Same track:",[2487,2488,2489,2495,2501],"ul",{},[2490,2491,2492,2494],"li",{},[18,2493,21],{"href":20}," — the parent guide covering project layout, config, and first deploy.",[2490,2496,2497,2500],{},[18,2498,2499],{"href":1788},"FastAPI vs Flask for API Development"," — the other framework decision worth making before you write code.",[2490,2502,2503,2506],{},[18,2504,2505],{"href":28},"Uvicorn vs Gunicorn Worker Configuration"," — worker counts move throughput more than framework choice does.",[14,2508,2509],{},[1693,2510,2511],{},"Other tracks:",[2487,2513,2514,2520],{},[2490,2515,2516,2519],{},[18,2517,2518],{"href":1585},"Calculating Cost per API Request"," — turn the benchmark ratios above into an actual monthly number.",[2490,2521,2522,2525],{},[18,2523,2524],{"href":1604},"Async Database Access with SQLAlchemy"," — where most of your request time really goes.",[2527,2528,2529],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":45,"searchDepth":64,"depth":64,"links":2531},[2532,2533,2534,2535,2536,2537,2538,2539,2540],{"id":34,"depth":64,"text":35},{"id":937,"depth":64,"text":938},{"id":1506,"depth":64,"text":1507},{"id":1597,"depth":64,"text":1598},{"id":1778,"depth":64,"text":1779},{"id":1905,"depth":64,"text":1906},{"id":2433,"depth":64,"text":2434},{"id":2445,"depth":64,"text":2446},{"id":2479,"depth":64,"text":2480},"FastAPI vs Litestar on the same endpoint: dependency injection models, msgspec validation speed, plugin depth, hiring reality, and when a rewrite pays.","md",{"pageTitle":2544,"type":2545,"datePublished":2546,"dateModified":2546},"FastAPI vs Litestar: Which Python API Framework Ships Faster","article","2026-07-23","\u002Fgetting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-litestar",{"title":5,"description":2541},"getting-started-with-python-apis-for-builders\u002Fsetting-up-fastapi\u002Ffastapi-vs-litestar\u002Findex","Ar9tiOQvTF3GtEEuC9rE9k58Zhg0liSVmkxVIyYQMEI",{"@context":2552,"@type":2553,"mainEntity":2554},"https:\u002F\u002Fschema.org","FAQPage",[2555,2560,2563,2566,2569],{"@type":2556,"name":2451,"acceptedAnswer":2557},"Question",{"@type":2558,"text":2559},"Answer","Only if serialization dominates your profile. At 20 million requests a month with a 6 ms database-bound handler, the framework difference is worth a few cents. On a 0.4 ms in-memory lookup at the same volume, msgspec can cut instance count by a third, which is real money. Profile before you port.",{"@type":2556,"name":2457,"acceptedAnswer":2561},{"@type":2558,"text":2562},"Low if you mount the Litestar app under a path prefix inside the existing FastAPI app and move one route group at a time. Same process, same deploy, instant rollback by unmounting. The risk concentrates in response-body drift, so diff both implementations against the same request corpus before cutting traffic over.",{"@type":2556,"name":2463,"acceptedAnswer":2564},{"@type":2558,"text":2565},"Yes. Litestar's Pydantic plugin accepts models as handler parameters and return types with no extra wiring, so a validator library carries over intact. You keep the routing and dependency injection improvements but give up most of the throughput advantage, since Pydantic remains the codec.",{"@type":2556,"name":2469,"acceptedAnswer":2567},{"@type":2558,"text":2568},"Measurably. The contractor pool that knows Litestar is a fraction of the FastAPI pool, and coding assistants routinely emit FastAPI syntax when asked for Litestar. Budget extra onboarding hours per engineer, and treat that as a recurring cost against whatever compute you save.",{"@type":2556,"name":2475,"acceptedAnswer":2570},{"@type":2558,"text":2571},"Litestar edges it. Return-type annotations are mandatory, so an undocumented endpoint fails at startup rather than shipping silently, and the schema includes examples derived from your types. FastAPI's output is perfectly good but lets you ship handlers with no declared response model.",1784887028457]