[{"data":1,"prerenderedAt":1719},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002F":152,"surround-\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002F":1717},[4,72],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,24,36,48,54,66],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation & Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators & Field Constraints in FastAPI & Pydantic V2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic: Production Patterns","\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",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"JSON Schema Customization","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[29,30],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Customizing OpenAPI Schema Generation in FastAPI: Production Implementation Guide","\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",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Mastering Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[41,42],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Handling Deeply Nested JSON Models Efficiently in FastAPI","\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",[47],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Performance Optimization for Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Pydantic V2 Migration Guide: FastAPI Production Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"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",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Type Hinting & IDE Integration in FastAPI: Advanced Pydantic Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[71],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[77,80,92,104,116,128,140],{"title":78,"path":74,"stem":79},"Core Architecture & Routing Patterns in FastAPI: A Production-Ready Blueprint","core-architecture-routing-patterns\u002Findex",{"title":81,"path":82,"stem":83,"children":84},"Application Factory Patterns in FastAPI: Production Architecture Guide","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[85,86],{"title":81,"path":82,"stem":83},{"title":87,"path":88,"stem":89,"children":90},"FastAPI App Factory Pattern for Testing and Deployment: Production Guide","\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",[91],{"title":87,"path":88,"stem":89},{"title":93,"path":94,"stem":95,"children":96},"Configuration Management in FastAPI: Production-Ready Patterns & Security","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[97,98],{"title":93,"path":94,"stem":95},{"title":99,"path":100,"stem":101,"children":102},"Managing Environment Variables with Pydantic Settings in FastAPI","\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",[103],{"title":99,"path":100,"stem":101},{"title":105,"path":106,"stem":107,"children":108},"Dependency Injection Strategies","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[109,110],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"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",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Error Handling & Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[121,122],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"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",[127],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Middleware Implementation","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[133,134],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Implementing Custom Middleware for Request Tracing in FastAPI","\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",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Modular Router Organization in FastAPI: Production-Grade Architecture","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[145,146],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"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",[151],{"title":147,"path":148,"stem":149},{"id":153,"title":87,"body":154,"description":1712,"extension":1713,"meta":1714,"navigation":313,"path":88,"seo":1715,"stem":89,"__hash__":1716},"content\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex.md",{"type":155,"value":156,"toc":1701},"minimark",[157,161,171,177,195,198,203,211,240,247,249,253,260,871,876,907,909,913,920,1265,1270,1293,1295,1299,1302,1307,1324,1334,1338,1512,1515,1517,1521,1638,1640,1644,1649,1652,1657,1679,1684,1697],[158,159,87],"h1",{"id":160},"fastapi-app-factory-pattern-for-testing-and-deployment-production-guide",[162,163,164,165,170],"p",{},"Monolithic FastAPI declarations often cause global state pollution, breaking test isolation and complicating multi-environment deployments. Implementing a ",[166,167,169],"a",{"href":168},"\u002Fcore-architecture-routing-patterns\u002F","Core Architecture & Routing Patterns"," compliant factory function decouples initialization, enabling fresh app instances per test run and seamless environment switching. This guide delivers immediate implementation steps, debugging workflows, and configuration strategies for production-grade APIs.",[162,172,173],{},[174,175,176],"strong",{},"Key Architectural Benefits:",[178,179,180,184,187],"ul",{},[181,182,183],"li",{},"Eliminates cross-test database contamination via dynamic instantiation",[181,185,186],{},"Centralizes environment-specific middleware and dependency injection",[181,188,189,190,194],{},"Aligns with proven ",[166,191,193],{"href":192},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002F","Application Factory Patterns"," for scalable microservices",[196,197],"hr",{},[199,200,202],"h2",{"id":201},"why-the-app-factory-pattern-solves-production-state-leaks","Why the App Factory Pattern Solves Production State Leaks",[162,204,205,206,210],{},"Static ",[207,208,209],"code",{},"app = FastAPI()"," declarations at module scope create shared references across the entire process. In asynchronous environments, this leads to:",[212,213,214,224,234],"ol",{},[181,215,216,219,220,223],{},[174,217,218],{},"Shared Async Client\u002FSession Leaks:"," HTTP clients, database connection pools, and WebSocket managers persist across test boundaries, causing ",[207,221,222],{},"ConnectionResetError"," or transaction rollbacks in subsequent tests.",[181,225,226,229,230,233],{},[174,227,228],{},"Middleware Pollution:"," Environment-specific middleware (e.g., rate limiting, debug logging, CORS origins) becomes hardcoded or requires fragile ",[207,231,232],{},"if ENV == \"test\""," guards.",[181,235,236,239],{},[174,237,238],{},"Router Mounting Conflicts:"," Re-importing modules with static apps triggers duplicate route registration warnings and breaks hot-reload workflows.",[162,241,242,243,246],{},"The factory pattern (",[207,244,245],{},"create_app()",") forces explicit instantiation. Each call returns an isolated object graph, guaranteeing that test suites, staging deployments, and production workers operate on independent state trees.",[196,248],{},[199,250,252],{"id":251},"implementing-the-factory-with-lifespan-and-config","Implementing the Factory with Lifespan and Config",[162,254,255,256,259],{},"Modern FastAPI relies on the ",[207,257,258],{},"lifespan"," context manager for deterministic async resource management. Pair this with Pydantic Settings to inject configuration without global reads.",[261,262,267],"pre",{"className":263,"code":264,"language":265,"meta":266,"style":266},"language-python shiki shiki-themes github-light","# app\u002Ffactory.py\nfrom contextlib import asynccontextmanager\nfrom typing import AsyncGenerator\n\nfrom fastapi import FastAPI\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\nfrom app.db import init_pool, close_pool, get_db\nfrom app.routers import api_router\n\nclass Settings(BaseSettings):\n APP_NAME: str = \"api-service\"\n ENV: str = \"development\"\n DB_URL: str = \"postgresql+asyncpg:\u002F\u002Fuser:pass@localhost:5432\u002Fdb\"\n DEBUG: bool = False\n \n model_config = SettingsConfigDict(env_file=\".env\", env_file_encoding=\"utf-8\")\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:\n # Startup: Validate secrets & initialize pools\n if app.state.settings.ENV == \"production\" and not app.state.settings.DB_URL.startswith(\"postgresql:\u002F\u002F\"):\n raise ValueError(\"Production DB_URL must use SSL\u002FTLS connection string.\")\n \n await init_pool(app.state.settings.DB_URL)\n yield\n # Shutdown: Graceful connection teardown\n await close_pool()\n\ndef create_app(settings: Settings | None = None) -> FastAPI:\n cfg = settings or Settings()\n \n app = FastAPI(\n title=cfg.APP_NAME,\n lifespan=lifespan,\n docs_url=\"\u002Fdocs\" if cfg.DEBUG else None,\n redoc_url=\"\u002Fredoc\" if cfg.DEBUG else None\n )\n \n # Inject config into app state for downstream access\n app.state.settings = cfg\n \n # Conditional middleware based on environment\n if cfg.DEBUG:\n from app.middleware import debug_logging_middleware\n app.middleware(\"http\")(debug_logging_middleware)\n \n app.include_router(api_router)\n return app\n","python","",[207,268,269,278,295,308,315,328,341,346,359,372,377,396,416,431,446,462,468,503,508,514,539,545,582,598,603,616,622,628,636,641,666,683,688,699,716,726,752,774,780,785,791,802,807,813,825,839,851,856,862],{"__ignoreMap":266},[270,271,274],"span",{"class":272,"line":273},"line",1,[270,275,277],{"class":276},"sAwPA","# app\u002Ffactory.py\n",[270,279,281,285,289,292],{"class":272,"line":280},2,[270,282,284],{"class":283},"sD7c4","from",[270,286,288],{"class":287},"sgsFI"," contextlib ",[270,290,291],{"class":283},"import",[270,293,294],{"class":287}," asynccontextmanager\n",[270,296,298,300,303,305],{"class":272,"line":297},3,[270,299,284],{"class":283},[270,301,302],{"class":287}," typing ",[270,304,291],{"class":283},[270,306,307],{"class":287}," AsyncGenerator\n",[270,309,311],{"class":272,"line":310},4,[270,312,314],{"emptyLinePlaceholder":313},true,"\n",[270,316,318,320,323,325],{"class":272,"line":317},5,[270,319,284],{"class":283},[270,321,322],{"class":287}," fastapi ",[270,324,291],{"class":283},[270,326,327],{"class":287}," FastAPI\n",[270,329,331,333,336,338],{"class":272,"line":330},6,[270,332,284],{"class":283},[270,334,335],{"class":287}," pydantic_settings ",[270,337,291],{"class":283},[270,339,340],{"class":287}," BaseSettings, SettingsConfigDict\n",[270,342,344],{"class":272,"line":343},7,[270,345,314],{"emptyLinePlaceholder":313},[270,347,349,351,354,356],{"class":272,"line":348},8,[270,350,284],{"class":283},[270,352,353],{"class":287}," app.db ",[270,355,291],{"class":283},[270,357,358],{"class":287}," init_pool, close_pool, get_db\n",[270,360,362,364,367,369],{"class":272,"line":361},9,[270,363,284],{"class":283},[270,365,366],{"class":287}," app.routers ",[270,368,291],{"class":283},[270,370,371],{"class":287}," api_router\n",[270,373,375],{"class":272,"line":374},10,[270,376,314],{"emptyLinePlaceholder":313},[270,378,380,383,387,390,393],{"class":272,"line":379},11,[270,381,382],{"class":283},"class",[270,384,386],{"class":385},"s7eDp"," Settings",[270,388,389],{"class":287},"(",[270,391,392],{"class":385},"BaseSettings",[270,394,395],{"class":287},"):\n",[270,397,399,403,406,409,412],{"class":272,"line":398},12,[270,400,402],{"class":401},"sYu0t"," APP_NAME",[270,404,405],{"class":287},": ",[270,407,408],{"class":401},"str",[270,410,411],{"class":283}," =",[270,413,415],{"class":414},"sYBdl"," \"api-service\"\n",[270,417,419,422,424,426,428],{"class":272,"line":418},13,[270,420,421],{"class":401}," ENV",[270,423,405],{"class":287},[270,425,408],{"class":401},[270,427,411],{"class":283},[270,429,430],{"class":414}," \"development\"\n",[270,432,434,437,439,441,443],{"class":272,"line":433},14,[270,435,436],{"class":401}," DB_URL",[270,438,405],{"class":287},[270,440,408],{"class":401},[270,442,411],{"class":283},[270,444,445],{"class":414}," \"postgresql+asyncpg:\u002F\u002Fuser:pass@localhost:5432\u002Fdb\"\n",[270,447,449,452,454,457,459],{"class":272,"line":448},15,[270,450,451],{"class":401}," DEBUG",[270,453,405],{"class":287},[270,455,456],{"class":401},"bool",[270,458,411],{"class":283},[270,460,461],{"class":401}," False\n",[270,463,465],{"class":272,"line":464},16,[270,466,467],{"class":287}," \n",[270,469,471,474,477,480,484,486,489,492,495,497,500],{"class":272,"line":470},17,[270,472,473],{"class":287}," model_config ",[270,475,476],{"class":283},"=",[270,478,479],{"class":287}," SettingsConfigDict(",[270,481,483],{"class":482},"sqxcx","env_file",[270,485,476],{"class":283},[270,487,488],{"class":414},"\".env\"",[270,490,491],{"class":287},", ",[270,493,494],{"class":482},"env_file_encoding",[270,496,476],{"class":283},[270,498,499],{"class":414},"\"utf-8\"",[270,501,502],{"class":287},")\n",[270,504,506],{"class":272,"line":505},18,[270,507,314],{"emptyLinePlaceholder":313},[270,509,511],{"class":272,"line":510},19,[270,512,513],{"class":385},"@asynccontextmanager\n",[270,515,517,520,523,526,529,532,534,536],{"class":272,"line":516},20,[270,518,519],{"class":283},"async",[270,521,522],{"class":283}," def",[270,524,525],{"class":385}," lifespan",[270,527,528],{"class":287},"(app: FastAPI) -> AsyncGenerator[",[270,530,531],{"class":401},"None",[270,533,491],{"class":287},[270,535,531],{"class":401},[270,537,538],{"class":287},"]:\n",[270,540,542],{"class":272,"line":541},21,[270,543,544],{"class":276}," # Startup: Validate secrets & initialize pools\n",[270,546,548,551,554,557,560,563,566,569,571,574,577,580],{"class":272,"line":547},22,[270,549,550],{"class":283}," if",[270,552,553],{"class":287}," app.state.settings.",[270,555,556],{"class":401},"ENV",[270,558,559],{"class":283}," ==",[270,561,562],{"class":414}," \"production\"",[270,564,565],{"class":283}," and",[270,567,568],{"class":283}," not",[270,570,553],{"class":287},[270,572,573],{"class":401},"DB_URL",[270,575,576],{"class":287},".startswith(",[270,578,579],{"class":414},"\"postgresql:\u002F\u002F\"",[270,581,395],{"class":287},[270,583,585,588,591,593,596],{"class":272,"line":584},23,[270,586,587],{"class":283}," raise",[270,589,590],{"class":401}," ValueError",[270,592,389],{"class":287},[270,594,595],{"class":414},"\"Production DB_URL must use SSL\u002FTLS connection string.\"",[270,597,502],{"class":287},[270,599,601],{"class":272,"line":600},24,[270,602,467],{"class":287},[270,604,606,609,612,614],{"class":272,"line":605},25,[270,607,608],{"class":283}," await",[270,610,611],{"class":287}," init_pool(app.state.settings.",[270,613,573],{"class":401},[270,615,502],{"class":287},[270,617,619],{"class":272,"line":618},26,[270,620,621],{"class":283}," yield\n",[270,623,625],{"class":272,"line":624},27,[270,626,627],{"class":276}," # Shutdown: Graceful connection teardown\n",[270,629,631,633],{"class":272,"line":630},28,[270,632,608],{"class":283},[270,634,635],{"class":287}," close_pool()\n",[270,637,639],{"class":272,"line":638},29,[270,640,314],{"emptyLinePlaceholder":313},[270,642,644,647,650,653,656,659,661,663],{"class":272,"line":643},30,[270,645,646],{"class":283},"def",[270,648,649],{"class":385}," create_app",[270,651,652],{"class":287},"(settings: Settings ",[270,654,655],{"class":283},"|",[270,657,658],{"class":401}," None",[270,660,411],{"class":283},[270,662,658],{"class":401},[270,664,665],{"class":287},") -> FastAPI:\n",[270,667,669,672,674,677,680],{"class":272,"line":668},31,[270,670,671],{"class":287}," cfg ",[270,673,476],{"class":283},[270,675,676],{"class":287}," settings ",[270,678,679],{"class":283},"or",[270,681,682],{"class":287}," Settings()\n",[270,684,686],{"class":272,"line":685},32,[270,687,467],{"class":287},[270,689,691,694,696],{"class":272,"line":690},33,[270,692,693],{"class":287}," app ",[270,695,476],{"class":283},[270,697,698],{"class":287}," FastAPI(\n",[270,700,702,705,707,710,713],{"class":272,"line":701},34,[270,703,704],{"class":482}," title",[270,706,476],{"class":283},[270,708,709],{"class":287},"cfg.",[270,711,712],{"class":401},"APP_NAME",[270,714,715],{"class":287},",\n",[270,717,719,721,723],{"class":272,"line":718},35,[270,720,525],{"class":482},[270,722,476],{"class":283},[270,724,725],{"class":287},"lifespan,\n",[270,727,729,732,734,737,739,742,745,748,750],{"class":272,"line":728},36,[270,730,731],{"class":482}," docs_url",[270,733,476],{"class":283},[270,735,736],{"class":414},"\"\u002Fdocs\"",[270,738,550],{"class":283},[270,740,741],{"class":287}," cfg.",[270,743,744],{"class":401},"DEBUG",[270,746,747],{"class":283}," else",[270,749,658],{"class":401},[270,751,715],{"class":287},[270,753,755,758,760,763,765,767,769,771],{"class":272,"line":754},37,[270,756,757],{"class":482}," redoc_url",[270,759,476],{"class":283},[270,761,762],{"class":414},"\"\u002Fredoc\"",[270,764,550],{"class":283},[270,766,741],{"class":287},[270,768,744],{"class":401},[270,770,747],{"class":283},[270,772,773],{"class":401}," None\n",[270,775,777],{"class":272,"line":776},38,[270,778,779],{"class":287}," )\n",[270,781,783],{"class":272,"line":782},39,[270,784,467],{"class":287},[270,786,788],{"class":272,"line":787},40,[270,789,790],{"class":276}," # Inject config into app state for downstream access\n",[270,792,794,797,799],{"class":272,"line":793},41,[270,795,796],{"class":287}," app.state.settings ",[270,798,476],{"class":283},[270,800,801],{"class":287}," cfg\n",[270,803,805],{"class":272,"line":804},42,[270,806,467],{"class":287},[270,808,810],{"class":272,"line":809},43,[270,811,812],{"class":276}," # Conditional middleware based on environment\n",[270,814,816,818,820,822],{"class":272,"line":815},44,[270,817,550],{"class":283},[270,819,741],{"class":287},[270,821,744],{"class":401},[270,823,824],{"class":287},":\n",[270,826,828,831,834,836],{"class":272,"line":827},45,[270,829,830],{"class":283}," from",[270,832,833],{"class":287}," app.middleware ",[270,835,291],{"class":283},[270,837,838],{"class":287}," debug_logging_middleware\n",[270,840,842,845,848],{"class":272,"line":841},46,[270,843,844],{"class":287}," app.middleware(",[270,846,847],{"class":414},"\"http\"",[270,849,850],{"class":287},")(debug_logging_middleware)\n",[270,852,854],{"class":272,"line":853},47,[270,855,467],{"class":287},[270,857,859],{"class":272,"line":858},48,[270,860,861],{"class":287}," app.include_router(api_router)\n",[270,863,865,868],{"class":272,"line":864},49,[270,866,867],{"class":283}," return",[270,869,870],{"class":287}," app\n",[162,872,873],{},[174,874,875],{},"Production Constraints Applied:",[178,877,878,891,901,904],{},[181,879,880,882,883,886,887,890],{},[207,881,258],{}," replaces deprecated ",[207,884,885],{},"@app.on_event(\"startup\")","\u002F",[207,888,889],{},"shutdown"," hooks",[181,892,893,896,897,900],{},[207,894,895],{},"app.state.settings"," avoids global ",[207,898,899],{},"os.environ"," reads inside route handlers",[181,902,903],{},"Docs endpoints are disabled in production to reduce attack surface",[181,905,906],{},"Fail-fast validation prevents CI\u002FCD pipelines from deploying misconfigured workers",[196,908],{},[199,910,912],{"id":911},"dependency-overrides-for-isolated-testing","Dependency Overrides for Isolated Testing",[162,914,915,916,919],{},"The ",[207,917,918],{},"app.dependency_overrides"," dictionary is the backbone of FastAPI test isolation. When paired with the factory pattern, it allows you to swap database sessions, auth providers, and external API clients without modifying production code.",[261,921,923],{"className":263,"code":922,"language":265,"meta":266,"style":266},"# tests\u002Fconftest.py\nimport pytest\nfrom fastapi.testclient import TestClient\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker\nfrom app.factory import create_app, Settings\nfrom app.db import get_db\n\n# In-memory SQLite for fast, isolated test execution\nTEST_DB_URL = \"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\"\n\nasync def override_get_db() -> AsyncGenerator[AsyncSession, None]:\n engine = create_async_engine(TEST_DB_URL, echo=False)\n async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)\n async with async_session() as session:\n yield session\n\n@pytest.fixture(scope=\"function\")\ndef test_app() -> FastAPI:\n # Fresh app instance per test function\n app = create_app(Settings(ENV=\"testing\", DB_URL=TEST_DB_URL, DEBUG=False))\n \n # Override production DB dependency\n app.dependency_overrides[get_db] = override_get_db\n \n yield app\n \n # CRITICAL: Clear overrides to prevent mock bleed into subsequent tests\n app.dependency_overrides.clear()\n\n@pytest.fixture(scope=\"function\")\ndef client(test_app: FastAPI) -> TestClient:\n # TestClient manages its own event loop; do not run it in async context\n with TestClient(test_app) as c:\n yield c\n",[207,924,925,930,937,949,961,973,984,988,993,1003,1007,1023,1047,1074,1091,1099,1103,1120,1130,1135,1170,1174,1179,1189,1193,1199,1203,1208,1213,1217,1231,1241,1246,1258],{"__ignoreMap":266},[270,926,927],{"class":272,"line":273},[270,928,929],{"class":276},"# tests\u002Fconftest.py\n",[270,931,932,934],{"class":272,"line":280},[270,933,291],{"class":283},[270,935,936],{"class":287}," pytest\n",[270,938,939,941,944,946],{"class":272,"line":297},[270,940,284],{"class":283},[270,942,943],{"class":287}," fastapi.testclient ",[270,945,291],{"class":283},[270,947,948],{"class":287}," TestClient\n",[270,950,951,953,956,958],{"class":272,"line":310},[270,952,284],{"class":283},[270,954,955],{"class":287}," sqlalchemy.ext.asyncio ",[270,957,291],{"class":283},[270,959,960],{"class":287}," AsyncSession, create_async_engine, async_sessionmaker\n",[270,962,963,965,968,970],{"class":272,"line":317},[270,964,284],{"class":283},[270,966,967],{"class":287}," app.factory ",[270,969,291],{"class":283},[270,971,972],{"class":287}," create_app, Settings\n",[270,974,975,977,979,981],{"class":272,"line":330},[270,976,284],{"class":283},[270,978,353],{"class":287},[270,980,291],{"class":283},[270,982,983],{"class":287}," get_db\n",[270,985,986],{"class":272,"line":343},[270,987,314],{"emptyLinePlaceholder":313},[270,989,990],{"class":272,"line":348},[270,991,992],{"class":276},"# In-memory SQLite for fast, isolated test execution\n",[270,994,995,998,1000],{"class":272,"line":361},[270,996,997],{"class":401},"TEST_DB_URL",[270,999,411],{"class":283},[270,1001,1002],{"class":414}," \"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\"\n",[270,1004,1005],{"class":272,"line":374},[270,1006,314],{"emptyLinePlaceholder":313},[270,1008,1009,1011,1013,1016,1019,1021],{"class":272,"line":379},[270,1010,519],{"class":283},[270,1012,522],{"class":283},[270,1014,1015],{"class":385}," override_get_db",[270,1017,1018],{"class":287},"() -> AsyncGenerator[AsyncSession, ",[270,1020,531],{"class":401},[270,1022,538],{"class":287},[270,1024,1025,1028,1030,1033,1035,1037,1040,1042,1045],{"class":272,"line":398},[270,1026,1027],{"class":287}," engine ",[270,1029,476],{"class":283},[270,1031,1032],{"class":287}," create_async_engine(",[270,1034,997],{"class":401},[270,1036,491],{"class":287},[270,1038,1039],{"class":482},"echo",[270,1041,476],{"class":283},[270,1043,1044],{"class":401},"False",[270,1046,502],{"class":287},[270,1048,1049,1052,1054,1057,1060,1062,1065,1068,1070,1072],{"class":272,"line":418},[270,1050,1051],{"class":287}," async_session ",[270,1053,476],{"class":283},[270,1055,1056],{"class":287}," async_sessionmaker(engine, ",[270,1058,1059],{"class":482},"class_",[270,1061,476],{"class":283},[270,1063,1064],{"class":287},"AsyncSession, ",[270,1066,1067],{"class":482},"expire_on_commit",[270,1069,476],{"class":283},[270,1071,1044],{"class":401},[270,1073,502],{"class":287},[270,1075,1076,1079,1082,1085,1088],{"class":272,"line":433},[270,1077,1078],{"class":283}," async",[270,1080,1081],{"class":283}," with",[270,1083,1084],{"class":287}," async_session() ",[270,1086,1087],{"class":283},"as",[270,1089,1090],{"class":287}," session:\n",[270,1092,1093,1096],{"class":272,"line":448},[270,1094,1095],{"class":283}," yield",[270,1097,1098],{"class":287}," session\n",[270,1100,1101],{"class":272,"line":464},[270,1102,314],{"emptyLinePlaceholder":313},[270,1104,1105,1108,1110,1113,1115,1118],{"class":272,"line":470},[270,1106,1107],{"class":385},"@pytest.fixture",[270,1109,389],{"class":287},[270,1111,1112],{"class":482},"scope",[270,1114,476],{"class":283},[270,1116,1117],{"class":414},"\"function\"",[270,1119,502],{"class":287},[270,1121,1122,1124,1127],{"class":272,"line":505},[270,1123,646],{"class":283},[270,1125,1126],{"class":385}," test_app",[270,1128,1129],{"class":287},"() -> FastAPI:\n",[270,1131,1132],{"class":272,"line":510},[270,1133,1134],{"class":276}," # Fresh app instance per test function\n",[270,1136,1137,1139,1141,1144,1146,1148,1151,1153,1155,1157,1159,1161,1163,1165,1167],{"class":272,"line":516},[270,1138,693],{"class":287},[270,1140,476],{"class":283},[270,1142,1143],{"class":287}," create_app(Settings(",[270,1145,556],{"class":482},[270,1147,476],{"class":283},[270,1149,1150],{"class":414},"\"testing\"",[270,1152,491],{"class":287},[270,1154,573],{"class":482},[270,1156,476],{"class":283},[270,1158,997],{"class":401},[270,1160,491],{"class":287},[270,1162,744],{"class":482},[270,1164,476],{"class":283},[270,1166,1044],{"class":401},[270,1168,1169],{"class":287},"))\n",[270,1171,1172],{"class":272,"line":541},[270,1173,467],{"class":287},[270,1175,1176],{"class":272,"line":547},[270,1177,1178],{"class":276}," # Override production DB dependency\n",[270,1180,1181,1184,1186],{"class":272,"line":584},[270,1182,1183],{"class":287}," app.dependency_overrides[get_db] ",[270,1185,476],{"class":283},[270,1187,1188],{"class":287}," override_get_db\n",[270,1190,1191],{"class":272,"line":600},[270,1192,467],{"class":287},[270,1194,1195,1197],{"class":272,"line":605},[270,1196,1095],{"class":283},[270,1198,870],{"class":287},[270,1200,1201],{"class":272,"line":618},[270,1202,467],{"class":287},[270,1204,1205],{"class":272,"line":624},[270,1206,1207],{"class":276}," # CRITICAL: Clear overrides to prevent mock bleed into subsequent tests\n",[270,1209,1210],{"class":272,"line":630},[270,1211,1212],{"class":287}," app.dependency_overrides.clear()\n",[270,1214,1215],{"class":272,"line":638},[270,1216,314],{"emptyLinePlaceholder":313},[270,1218,1219,1221,1223,1225,1227,1229],{"class":272,"line":643},[270,1220,1107],{"class":385},[270,1222,389],{"class":287},[270,1224,1112],{"class":482},[270,1226,476],{"class":283},[270,1228,1117],{"class":414},[270,1230,502],{"class":287},[270,1232,1233,1235,1238],{"class":272,"line":668},[270,1234,646],{"class":283},[270,1236,1237],{"class":385}," client",[270,1239,1240],{"class":287},"(test_app: FastAPI) -> TestClient:\n",[270,1242,1243],{"class":272,"line":685},[270,1244,1245],{"class":276}," # TestClient manages its own event loop; do not run it in async context\n",[270,1247,1248,1250,1253,1255],{"class":272,"line":690},[270,1249,1081],{"class":283},[270,1251,1252],{"class":287}," TestClient(test_app) ",[270,1254,1087],{"class":283},[270,1256,1257],{"class":287}," c:\n",[270,1259,1260,1262],{"class":272,"line":701},[270,1261,1095],{"class":283},[270,1263,1264],{"class":287}," c\n",[162,1266,1267],{},[174,1268,1269],{},"Testing Best Practices:",[178,1271,1272,1283,1290],{},[181,1273,1274,1275,1278,1279,1282],{},"Use ",[207,1276,1277],{},"scope=\"function\""," for ",[207,1280,1281],{},"test_app"," to guarantee zero state leakage between test cases",[181,1284,1285,1286,1289],{},"Always instantiate ",[207,1287,1288],{},"TestClient"," inside a context manager to handle startup\u002Fshutdown hooks correctly",[181,1291,1292],{},"Validate override scope: Only override dependencies explicitly used by the route under test",[196,1294],{},[199,1296,1298],{"id":1297},"deployment-configuration-environment-switching","Deployment Configuration & Environment Switching",[162,1300,1301],{},"Factory parameters map directly to container orchestration and CI\u002FCD pipelines. Avoid environment detection magic; pass explicit flags.",[1303,1304,1306],"h3",{"id":1305},"docker-uvicorn-worker-configuration","Docker & Uvicorn Worker Configuration",[261,1308,1312],{"className":1309,"code":1310,"language":1311,"meta":266,"style":266},"language-dockerfile shiki shiki-themes github-light","# Dockerfile snippet\nCMD [\"uvicorn\", \"app.factory:create_app\", \"--factory\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\", \"--workers\", \"4\"]\n","dockerfile",[207,1313,1314,1319],{"__ignoreMap":266},[270,1315,1316],{"class":272,"line":273},[270,1317,1318],{},"# Dockerfile snippet\n",[270,1320,1321],{"class":272,"line":280},[270,1322,1323],{},"CMD [\"uvicorn\", \"app.factory:create_app\", \"--factory\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\", \"--workers\", \"4\"]\n",[162,1325,1326,1327,1330,1331,1333],{},"Using ",[207,1328,1329],{},"--factory"," tells Uvicorn to call ",[207,1332,245],{}," once per worker process. This prevents module-level side effects from executing during import.",[1303,1335,1337],{"id":1336},"health-check-implementation","Health Check Implementation",[261,1339,1341],{"className":263,"code":1340,"language":265,"meta":266,"style":266},"# app\u002Frouters\u002Fhealth.py\nfrom fastapi import APIRouter, Depends\nfrom app.db import get_db\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nrouter = APIRouter()\n\n@router.get(\"\u002Fhealth\")\nasync def health_check(db: AsyncSession = Depends(get_db)):\n try:\n await db.execute(\"SELECT 1\")\n return {\"status\": \"healthy\", \"db\": \"connected\"}\n except Exception:\n return {\"status\": \"degraded\", \"db\": \"disconnected\"}, 503\n",[207,1342,1343,1348,1359,1369,1380,1384,1394,1398,1410,1427,1434,1446,1474,1484],{"__ignoreMap":266},[270,1344,1345],{"class":272,"line":273},[270,1346,1347],{"class":276},"# app\u002Frouters\u002Fhealth.py\n",[270,1349,1350,1352,1354,1356],{"class":272,"line":280},[270,1351,284],{"class":283},[270,1353,322],{"class":287},[270,1355,291],{"class":283},[270,1357,1358],{"class":287}," APIRouter, Depends\n",[270,1360,1361,1363,1365,1367],{"class":272,"line":297},[270,1362,284],{"class":283},[270,1364,353],{"class":287},[270,1366,291],{"class":283},[270,1368,983],{"class":287},[270,1370,1371,1373,1375,1377],{"class":272,"line":310},[270,1372,284],{"class":283},[270,1374,955],{"class":287},[270,1376,291],{"class":283},[270,1378,1379],{"class":287}," AsyncSession\n",[270,1381,1382],{"class":272,"line":317},[270,1383,314],{"emptyLinePlaceholder":313},[270,1385,1386,1389,1391],{"class":272,"line":330},[270,1387,1388],{"class":287},"router ",[270,1390,476],{"class":283},[270,1392,1393],{"class":287}," APIRouter()\n",[270,1395,1396],{"class":272,"line":343},[270,1397,314],{"emptyLinePlaceholder":313},[270,1399,1400,1403,1405,1408],{"class":272,"line":348},[270,1401,1402],{"class":385},"@router.get",[270,1404,389],{"class":287},[270,1406,1407],{"class":414},"\"\u002Fhealth\"",[270,1409,502],{"class":287},[270,1411,1412,1414,1416,1419,1422,1424],{"class":272,"line":361},[270,1413,519],{"class":283},[270,1415,522],{"class":283},[270,1417,1418],{"class":385}," health_check",[270,1420,1421],{"class":287},"(db: AsyncSession ",[270,1423,476],{"class":283},[270,1425,1426],{"class":287}," Depends(get_db)):\n",[270,1428,1429,1432],{"class":272,"line":374},[270,1430,1431],{"class":283}," try",[270,1433,824],{"class":287},[270,1435,1436,1438,1441,1444],{"class":272,"line":379},[270,1437,608],{"class":283},[270,1439,1440],{"class":287}," db.execute(",[270,1442,1443],{"class":414},"\"SELECT 1\"",[270,1445,502],{"class":287},[270,1447,1448,1450,1453,1456,1458,1461,1463,1466,1468,1471],{"class":272,"line":398},[270,1449,867],{"class":283},[270,1451,1452],{"class":287}," {",[270,1454,1455],{"class":414},"\"status\"",[270,1457,405],{"class":287},[270,1459,1460],{"class":414},"\"healthy\"",[270,1462,491],{"class":287},[270,1464,1465],{"class":414},"\"db\"",[270,1467,405],{"class":287},[270,1469,1470],{"class":414},"\"connected\"",[270,1472,1473],{"class":287},"}\n",[270,1475,1476,1479,1482],{"class":272,"line":418},[270,1477,1478],{"class":283}," except",[270,1480,1481],{"class":401}," Exception",[270,1483,824],{"class":287},[270,1485,1486,1488,1490,1492,1494,1497,1499,1501,1503,1506,1509],{"class":272,"line":433},[270,1487,867],{"class":283},[270,1489,1452],{"class":287},[270,1491,1455],{"class":414},[270,1493,405],{"class":287},[270,1495,1496],{"class":414},"\"degraded\"",[270,1498,491],{"class":287},[270,1500,1465],{"class":414},[270,1502,405],{"class":287},[270,1504,1505],{"class":414},"\"disconnected\"",[270,1507,1508],{"class":287},"}, ",[270,1510,1511],{"class":401},"503\n",[162,1513,1514],{},"Health checks verify that factory-injected state (DB pools, cache clients) initialized correctly. Configure Kubernetes\u002Fliveness probes to hit this endpoint.",[196,1516],{},[199,1518,1520],{"id":1519},"common-production-pitfalls-fixes","Common Production Pitfalls & Fixes",[1522,1523,1524,1540],"table",{},[1525,1526,1527],"thead",{},[1528,1529,1530,1534,1537],"tr",{},[1531,1532,1533],"th",{},"Issue",[1531,1535,1536],{},"Root Cause",[1531,1538,1539],{},"Production Fix",[1541,1542,1543,1567,1593,1617],"tbody",{},[1528,1544,1545,1554,1557],{},[1546,1547,1548],"td",{},[174,1549,1550,1551,1553],{},"Reusing a single ",[207,1552,1288],{}," across test classes",[1546,1555,1556],{},"Shared HTTP session retains cookies, auth tokens, and DB connections",[1546,1558,1559,1560,1563,1564,1566],{},"Instantiate ",[207,1561,1562],{},"TestClient(create_app())"," per test module or use ",[207,1565,1277],{}," fixtures with explicit teardown",[1528,1568,1569,1577,1584],{},[1546,1570,1571],{},[174,1572,1573,1574],{},"Hardcoding environment variables in ",[207,1575,1576],{},"create_app",[1546,1578,1579,1580,1583],{},"Breaks CI\u002FCD parity; forces manual ",[207,1581,1582],{},".env"," management",[1546,1585,1274,1586,1589,1590,1592],{},[207,1587,1588],{},"pydantic-settings"," with ",[207,1591,1582],{}," fallbacks and explicit factory arguments for deterministic testing",[1528,1594,1595,1600,1603],{},[1546,1596,1597],{},[174,1598,1599],{},"Forgetting to clear dependency overrides",[1546,1601,1602],{},"Mocks persist into subsequent tests, causing false positives\u002Fnegatives",[1546,1604,1605,1606,1609,1610,1613,1614],{},"Always call ",[207,1607,1608],{},"app.dependency_overrides.clear()"," in a ",[207,1611,1612],{},"yield"," teardown or ",[207,1615,1616],{},"pytest_finalizer",[1528,1618,1619,1626,1629],{},[1546,1620,1621],{},[174,1622,1623,1624],{},"Mounting routers inside ",[207,1625,258],{},[1546,1627,1628],{},"Routes register after startup, breaking OpenAPI schema generation",[1546,1630,1631,1632,1634,1635,1637],{},"Mount routers synchronously in ",[207,1633,245],{},"; reserve ",[207,1636,258],{}," strictly for resource init\u002Fteardown",[196,1639],{},[199,1641,1643],{"id":1642},"frequently-asked-questions","Frequently Asked Questions",[162,1645,1646],{},[174,1647,1648],{},"Does the app factory pattern impact FastAPI startup performance?",[162,1650,1651],{},"Negligible. Initialization occurs once per worker process. The dynamic routing overhead is measured in microseconds and is heavily offset by improved test isolation, faster CI feedback loops, and deployment reliability.",[162,1653,1654],{},[174,1655,1656],{},"How do I handle OpenAPI docs with multiple factory instances?",[162,1658,1659,1660,491,1663,1666,1667,1670,1671,1674,1675,1678],{},"Pass ",[207,1661,1662],{},"title",[207,1664,1665],{},"version",", and ",[207,1668,1669],{},"description"," dynamically via the factory's ",[207,1672,1673],{},"Settings"," object. FastAPI auto-generates the OpenAPI schema per instance without cross-app conflicts. Ensure ",[207,1676,1677],{},"openapi_url"," is consistent if using API gateways.",[162,1680,1681],{},[174,1682,1683],{},"Can I use the factory pattern with FastAPI's lifespan events?",[162,1685,1686,1687,1689,1690,1693,1694,1696],{},"Yes. ",[207,1688,258],{}," is explicitly designed for factory patterns. Define it as a standalone ",[207,1691,1692],{},"@asynccontextmanager"," or alongside ",[207,1695,245],{}," to ensure database pools, cache clients, and background tasks bind to the correct instance. Never attach lifespan to a globally instantiated app.",[1698,1699,1700],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}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":266,"searchDepth":280,"depth":280,"links":1702},[1703,1704,1705,1706,1710,1711],{"id":201,"depth":280,"text":202},{"id":251,"depth":280,"text":252},{"id":911,"depth":280,"text":912},{"id":1297,"depth":280,"text":1298,"children":1707},[1708,1709],{"id":1305,"depth":297,"text":1306},{"id":1336,"depth":297,"text":1337},{"id":1519,"depth":280,"text":1520},{"id":1642,"depth":280,"text":1643},"Monolithic FastAPI declarations often cause global state pollution, breaking test isolation and complicating multi-environment deployments. Implementing a…","md",{},{"title":87,"description":1712},"t76B-0YfjXlHqgqnIImT0VKdFQo8Bi6zCyP6SaIw7ck",[1718,1718],null,1778082655308]