[{"data":1,"prerenderedAt":1182},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F":1180},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"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",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"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",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"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",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"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",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":129,"body":312,"description":1132,"extension":1133,"meta":1134,"navigation":434,"path":130,"seo":1178,"stem":131,"__hash__":1179},"content\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex.md",{"type":313,"value":314,"toc":1119},"minimark",[315,319,326,358,372,377,380,384,392,396,401,576,580,752,756,870,874,965,969,998,1002,1088,1092,1115],[316,317,129],"h1",{"id":318},"async-sqlalchemy-session-per-request-in-fastapi",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,338,345,348,355],"ul",{},[330,331,332,333,337],"li",{},"Build the async engine and ",[334,335,336],"code",{},"async_sessionmaker"," once in lifespan; they own the pool.",[330,339,340,341,344],{},"Provide a fresh session per request through a ",[334,342,343],{},"yield"," dependency.",[330,346,347],{},"Commit on success and roll back on error inside the dependency.",[330,349,350,351,354],{},"Alias it as ",[334,352,353],{},"SessionDep"," so handlers stay clean and typed.",[330,356,357],{},"Override the dependency in tests for full isolation.",[320,359,360,361,366,367,371],{},"This guide is the concrete setup behind ",[362,363,365],"a",{"href":364},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F","Async Database Sessions",", and it applies the ",[362,368,370],{"href":369},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","dependency injection"," yield pattern to databases.",[373,374,376],"h2",{"id":375},"the-problem-this-solves","The Problem This Solves",[320,378,379],{},"Sharing a session across requests corrupts transaction state, and opening one without disciplined cleanup leaks connections. A request-scoped session provided by a yield dependency gives each request its own isolated, automatically-closed transaction.",[373,381,383],{"id":382},"prerequisites","Prerequisites",[327,385,386,389],{},[330,387,388],{},"SQLAlchemy async with an async driver such as asyncpg.",[330,390,391],{},"A FastAPI app using a lifespan and dependency injection.",[373,393,395],{"id":394},"step-by-step-implementation","Step-by-Step Implementation",[397,398,400],"h3",{"id":399},"_1-engine-and-factory-in-lifespan","1. Engine and factory in lifespan",[402,403,408],"pre",{"className":404,"code":405,"language":406,"meta":407,"style":407},"language-python shiki shiki-themes github-light","from contextlib import asynccontextmanager\n\nfrom fastapi import FastAPI\nfrom sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    engine = create_async_engine(app.state.settings.database_url,\n                                 pool_size=10, max_overflow=5)\n    # expire_on_commit=False keeps attributes usable after commit during serialization.\n    app.state.session_factory = async_sessionmaker(engine, expire_on_commit=False)\n    yield\n    await engine.dispose()\n","python","",[334,409,410,429,436,449,462,467,472,479,494,506,533,540,561,567],{"__ignoreMap":407},[411,412,415,419,423,426],"span",{"class":413,"line":414},"line",1,[411,416,418],{"class":417},"sD7c4","from",[411,420,422],{"class":421},"sgsFI"," contextlib ",[411,424,425],{"class":417},"import",[411,427,428],{"class":421}," asynccontextmanager\n",[411,430,432],{"class":413,"line":431},2,[411,433,435],{"emptyLinePlaceholder":434},true,"\n",[411,437,439,441,444,446],{"class":413,"line":438},3,[411,440,418],{"class":417},[411,442,443],{"class":421}," fastapi ",[411,445,425],{"class":417},[411,447,448],{"class":421}," FastAPI\n",[411,450,452,454,457,459],{"class":413,"line":451},4,[411,453,418],{"class":417},[411,455,456],{"class":421}," sqlalchemy.ext.asyncio ",[411,458,425],{"class":417},[411,460,461],{"class":421}," async_sessionmaker, create_async_engine\n",[411,463,465],{"class":413,"line":464},5,[411,466,435],{"emptyLinePlaceholder":434},[411,468,470],{"class":413,"line":469},6,[411,471,435],{"emptyLinePlaceholder":434},[411,473,475],{"class":413,"line":474},7,[411,476,478],{"class":477},"s7eDp","@asynccontextmanager\n",[411,480,482,485,488,491],{"class":413,"line":481},8,[411,483,484],{"class":417},"async",[411,486,487],{"class":417}," def",[411,489,490],{"class":477}," lifespan",[411,492,493],{"class":421},"(app: FastAPI):\n",[411,495,497,500,503],{"class":413,"line":496},9,[411,498,499],{"class":421},"    engine ",[411,501,502],{"class":417},"=",[411,504,505],{"class":421}," create_async_engine(app.state.settings.database_url,\n",[411,507,509,513,515,519,522,525,527,530],{"class":413,"line":508},10,[411,510,512],{"class":511},"sqxcx","                                 pool_size",[411,514,502],{"class":417},[411,516,518],{"class":517},"sYu0t","10",[411,520,521],{"class":421},", ",[411,523,524],{"class":511},"max_overflow",[411,526,502],{"class":417},[411,528,529],{"class":517},"5",[411,531,532],{"class":421},")\n",[411,534,536],{"class":413,"line":535},11,[411,537,539],{"class":538},"sAwPA","    # expire_on_commit=False keeps attributes usable after commit during serialization.\n",[411,541,543,546,548,551,554,556,559],{"class":413,"line":542},12,[411,544,545],{"class":421},"    app.state.session_factory ",[411,547,502],{"class":417},[411,549,550],{"class":421}," async_sessionmaker(engine, ",[411,552,553],{"class":511},"expire_on_commit",[411,555,502],{"class":417},[411,557,558],{"class":517},"False",[411,560,532],{"class":421},[411,562,564],{"class":413,"line":563},13,[411,565,566],{"class":417},"    yield\n",[411,568,570,573],{"class":413,"line":569},14,[411,571,572],{"class":417},"    await",[411,574,575],{"class":421}," engine.dispose()\n",[397,577,579],{"id":578},"_2-the-session-dependency","2. The session dependency",[402,581,583],{"className":404,"code":582,"language":406,"meta":407,"style":407},"from collections.abc import AsyncGenerator\nfrom typing import Annotated\n\nfrom fastapi import Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\n\nasync def get_session(request: Request) -> AsyncGenerator[AsyncSession, None]:\n    async with request.app.state.session_factory() as session:\n        try:\n            yield session\n            await session.commit()      # Commit once on success.\n        except Exception:\n            await session.rollback()    # Roll back on any error.\n            raise\n\n\nSessionDep = Annotated[AsyncSession, Depends(get_session)]\n",[334,584,585,597,609,613,624,635,639,643,661,678,686,694,705,715,725,731,736,741],{"__ignoreMap":407},[411,586,587,589,592,594],{"class":413,"line":414},[411,588,418],{"class":417},[411,590,591],{"class":421}," collections.abc ",[411,593,425],{"class":417},[411,595,596],{"class":421}," AsyncGenerator\n",[411,598,599,601,604,606],{"class":413,"line":431},[411,600,418],{"class":417},[411,602,603],{"class":421}," typing ",[411,605,425],{"class":417},[411,607,608],{"class":421}," Annotated\n",[411,610,611],{"class":413,"line":438},[411,612,435],{"emptyLinePlaceholder":434},[411,614,615,617,619,621],{"class":413,"line":451},[411,616,418],{"class":417},[411,618,443],{"class":421},[411,620,425],{"class":417},[411,622,623],{"class":421}," Depends, Request\n",[411,625,626,628,630,632],{"class":413,"line":464},[411,627,418],{"class":417},[411,629,456],{"class":421},[411,631,425],{"class":417},[411,633,634],{"class":421}," AsyncSession\n",[411,636,637],{"class":413,"line":469},[411,638,435],{"emptyLinePlaceholder":434},[411,640,641],{"class":413,"line":474},[411,642,435],{"emptyLinePlaceholder":434},[411,644,645,647,649,652,655,658],{"class":413,"line":481},[411,646,484],{"class":417},[411,648,487],{"class":417},[411,650,651],{"class":477}," get_session",[411,653,654],{"class":421},"(request: Request) -> AsyncGenerator[AsyncSession, ",[411,656,657],{"class":517},"None",[411,659,660],{"class":421},"]:\n",[411,662,663,666,669,672,675],{"class":413,"line":496},[411,664,665],{"class":417},"    async",[411,667,668],{"class":417}," with",[411,670,671],{"class":421}," request.app.state.session_factory() ",[411,673,674],{"class":417},"as",[411,676,677],{"class":421}," session:\n",[411,679,680,683],{"class":413,"line":508},[411,681,682],{"class":417},"        try",[411,684,685],{"class":421},":\n",[411,687,688,691],{"class":413,"line":535},[411,689,690],{"class":417},"            yield",[411,692,693],{"class":421}," session\n",[411,695,696,699,702],{"class":413,"line":542},[411,697,698],{"class":417},"            await",[411,700,701],{"class":421}," session.commit()      ",[411,703,704],{"class":538},"# Commit once on success.\n",[411,706,707,710,713],{"class":413,"line":563},[411,708,709],{"class":417},"        except",[411,711,712],{"class":517}," Exception",[411,714,685],{"class":421},[411,716,717,719,722],{"class":413,"line":569},[411,718,698],{"class":417},[411,720,721],{"class":421}," session.rollback()    ",[411,723,724],{"class":538},"# Roll back on any error.\n",[411,726,728],{"class":413,"line":727},15,[411,729,730],{"class":417},"            raise\n",[411,732,734],{"class":413,"line":733},16,[411,735,435],{"emptyLinePlaceholder":434},[411,737,739],{"class":413,"line":738},17,[411,740,435],{"emptyLinePlaceholder":434},[411,742,744,747,749],{"class":413,"line":743},18,[411,745,746],{"class":421},"SessionDep ",[411,748,502],{"class":417},[411,750,751],{"class":421}," Annotated[AsyncSession, Depends(get_session)]\n",[397,753,755],{"id":754},"_3-use-it-in-a-handler","3. Use it in a handler",[402,757,759],{"className":404,"code":758,"language":406,"meta":407,"style":407},"from fastapi import APIRouter\n\nrouter = APIRouter()\n\n\n@router.get(\"\u002Fusers\u002F{user_id}\")\nasync def get_user(user_id: int, session: SessionDep) -> dict:\n    user = await session.get(User, user_id)\n    return {\"id\": user.id, \"email\": user.email}\n",[334,760,761,772,776,786,790,794,814,837,850],{"__ignoreMap":407},[411,762,763,765,767,769],{"class":413,"line":414},[411,764,418],{"class":417},[411,766,443],{"class":421},[411,768,425],{"class":417},[411,770,771],{"class":421}," APIRouter\n",[411,773,774],{"class":413,"line":431},[411,775,435],{"emptyLinePlaceholder":434},[411,777,778,781,783],{"class":413,"line":438},[411,779,780],{"class":421},"router ",[411,782,502],{"class":417},[411,784,785],{"class":421}," APIRouter()\n",[411,787,788],{"class":413,"line":451},[411,789,435],{"emptyLinePlaceholder":434},[411,791,792],{"class":413,"line":464},[411,793,435],{"emptyLinePlaceholder":434},[411,795,796,799,802,806,809,812],{"class":413,"line":469},[411,797,798],{"class":477},"@router.get",[411,800,801],{"class":421},"(",[411,803,805],{"class":804},"sYBdl","\"\u002Fusers\u002F",[411,807,808],{"class":517},"{user_id}",[411,810,811],{"class":804},"\"",[411,813,532],{"class":421},[411,815,816,818,820,823,826,829,832,835],{"class":413,"line":474},[411,817,484],{"class":417},[411,819,487],{"class":417},[411,821,822],{"class":477}," get_user",[411,824,825],{"class":421},"(user_id: ",[411,827,828],{"class":517},"int",[411,830,831],{"class":421},", session: SessionDep) -> ",[411,833,834],{"class":517},"dict",[411,836,685],{"class":421},[411,838,839,842,844,847],{"class":413,"line":481},[411,840,841],{"class":421},"    user ",[411,843,502],{"class":417},[411,845,846],{"class":417}," await",[411,848,849],{"class":421}," session.get(User, user_id)\n",[411,851,852,855,858,861,864,867],{"class":413,"line":496},[411,853,854],{"class":417},"    return",[411,856,857],{"class":421}," {",[411,859,860],{"class":804},"\"id\"",[411,862,863],{"class":421},": user.id, ",[411,865,866],{"class":804},"\"email\"",[411,868,869],{"class":421},": user.email}\n",[397,871,873],{"id":872},"_4-override-it-in-tests","4. Override it in tests",[402,875,877],{"className":404,"code":876,"language":406,"meta":407,"style":407},"@pytest.fixture\ndef client(app, test_session_factory):\n    async def _test_session():\n        async with test_session_factory() as s:\n            yield s\n    app.dependency_overrides[get_session] = _test_session\n    with TestClient(app) as c:\n        yield c\n    app.dependency_overrides.clear()\n",[334,878,879,884,895,907,922,929,939,952,960],{"__ignoreMap":407},[411,880,881],{"class":413,"line":414},[411,882,883],{"class":477},"@pytest.fixture\n",[411,885,886,889,892],{"class":413,"line":431},[411,887,888],{"class":417},"def",[411,890,891],{"class":477}," client",[411,893,894],{"class":421},"(app, test_session_factory):\n",[411,896,897,899,901,904],{"class":413,"line":438},[411,898,665],{"class":417},[411,900,487],{"class":417},[411,902,903],{"class":477}," _test_session",[411,905,906],{"class":421},"():\n",[411,908,909,912,914,917,919],{"class":413,"line":451},[411,910,911],{"class":417},"        async",[411,913,668],{"class":417},[411,915,916],{"class":421}," test_session_factory() ",[411,918,674],{"class":417},[411,920,921],{"class":421}," s:\n",[411,923,924,926],{"class":413,"line":464},[411,925,690],{"class":417},[411,927,928],{"class":421}," s\n",[411,930,931,934,936],{"class":413,"line":469},[411,932,933],{"class":421},"    app.dependency_overrides[get_session] ",[411,935,502],{"class":417},[411,937,938],{"class":421}," _test_session\n",[411,940,941,944,947,949],{"class":413,"line":474},[411,942,943],{"class":417},"    with",[411,945,946],{"class":421}," TestClient(app) ",[411,948,674],{"class":417},[411,950,951],{"class":421}," c:\n",[411,953,954,957],{"class":413,"line":481},[411,955,956],{"class":417},"        yield",[411,958,959],{"class":421}," c\n",[411,961,962],{"class":413,"line":496},[411,963,964],{"class":421},"    app.dependency_overrides.clear()\n",[373,966,968],{"id":967},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,970,971,982,992],{},[330,972,973,978,979,981],{},[323,974,975,977],{},[334,976,553],{},"."," Leave it ",[334,980,558],{}," so model attributes remain accessible during response serialization after commit.",[330,983,984,987,988,991],{},[323,985,986],{},"Nested transactions."," For multi-step units of work, use explicit ",[334,989,990],{},"session.begin()"," blocks rather than relying solely on the dependency's commit.",[330,993,994,997],{},[323,995,996],{},"Background jobs."," Workers create their own session factory; do not reuse the request session outside the request.",[373,999,1001],{"id":1000},"verification","Verification",[402,1003,1005],{"className":404,"code":1004,"language":406,"meta":407,"style":407},"async def test_rolls_back_on_error(session):\n    with pytest.raises(ValueError):\n        async with session.begin():\n            session.add(User(email=\"x@y.z\"))\n            raise ValueError(\"boom\")\n    assert await session.scalar(select(func.count()).select_from(User)) == 0\n",[334,1006,1007,1019,1032,1041,1057,1072],{"__ignoreMap":407},[411,1008,1009,1011,1013,1016],{"class":413,"line":414},[411,1010,484],{"class":417},[411,1012,487],{"class":417},[411,1014,1015],{"class":477}," test_rolls_back_on_error",[411,1017,1018],{"class":421},"(session):\n",[411,1020,1021,1023,1026,1029],{"class":413,"line":431},[411,1022,943],{"class":417},[411,1024,1025],{"class":421}," pytest.raises(",[411,1027,1028],{"class":517},"ValueError",[411,1030,1031],{"class":421},"):\n",[411,1033,1034,1036,1038],{"class":413,"line":438},[411,1035,911],{"class":417},[411,1037,668],{"class":417},[411,1039,1040],{"class":421}," session.begin():\n",[411,1042,1043,1046,1049,1051,1054],{"class":413,"line":451},[411,1044,1045],{"class":421},"            session.add(User(",[411,1047,1048],{"class":511},"email",[411,1050,502],{"class":417},[411,1052,1053],{"class":804},"\"x@y.z\"",[411,1055,1056],{"class":421},"))\n",[411,1058,1059,1062,1065,1067,1070],{"class":413,"line":464},[411,1060,1061],{"class":417},"            raise",[411,1063,1064],{"class":517}," ValueError",[411,1066,801],{"class":421},[411,1068,1069],{"class":804},"\"boom\"",[411,1071,532],{"class":421},[411,1073,1074,1077,1079,1082,1085],{"class":413,"line":469},[411,1075,1076],{"class":417},"    assert",[411,1078,846],{"class":417},[411,1080,1081],{"class":421}," session.scalar(select(func.count()).select_from(User)) ",[411,1083,1084],{"class":417},"==",[411,1086,1087],{"class":517}," 0\n",[373,1089,1091],{"id":1090},"related-reading","Related Reading",[327,1093,1094,1102],{},[330,1095,1096,1099,1100,977],{},[323,1097,1098],{},"Up to the topic:"," ",[362,1101,365],{"href":364},[330,1103,1104,1099,1107,1111,1112,977],{},[323,1105,1106],{},"Related guides:",[362,1108,1110],{"href":1109},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F","Fixing asyncpg Pool Exhaustion"," and ",[362,1113,257],{"href":1114},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002F",[1116,1117,1118],"style",{},"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 .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}",{"title":407,"searchDepth":431,"depth":431,"links":1120},[1121,1122,1123,1129,1130,1131],{"id":375,"depth":431,"text":376},{"id":382,"depth":431,"text":383},{"id":394,"depth":431,"text":395,"children":1124},[1125,1126,1127,1128],{"id":399,"depth":438,"text":400},{"id":578,"depth":438,"text":579},{"id":754,"depth":438,"text":755},{"id":872,"depth":438,"text":873},{"id":967,"depth":431,"text":968},{"id":1000,"depth":431,"text":1001},{"id":1090,"depth":431,"text":1091},"Wire a request-scoped async SQLAlchemy session in FastAPI: engine and sessionmaker in lifespan, a yield dependency that commits or rolls back, and clean overrides in tests.","md",{"slug":1135,"type":1136,"breadcrumb":1137,"datePublished":1148,"dateModified":1149,"howto":1150,"faq":1168},"async-sqlalchemy-session-per-request","long_tail",[1138,1141,1144,1145],{"label":1139,"path":1140},"Home","\u002F",{"label":1142,"path":1143},"Async, Background Tasks & Observability","\u002Fasync-background-tasks-observability\u002F",{"label":365,"path":364},{"label":1146,"path":1147},"Async SQLAlchemy Session per Request","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F","2026-02-24","2026-06-18",{"name":1151,"steps":1152},"Provide an async SQLAlchemy session per request in FastAPI",[1153,1156,1159,1162,1165],{"name":1154,"text":1155},"Create the engine in lifespan","Build the async engine and async_sessionmaker once at startup and store them on app.state.",{"name":1157,"text":1158},"Write a yield dependency","Acquire a session per request, yield it, then commit on success or roll back on error.",{"name":1160,"text":1161},"Alias the dependency","Expose an Annotated SessionDep so handlers stay clean and typed.",{"name":1163,"text":1164},"Use it in handlers","Inject SessionDep and run awaited queries within the request scope.",{"name":1166,"text":1167},"Override it in tests","Replace the session dependency with a test session and clear it afterward.",[1169,1172,1175],{"q":1170,"a":1171},"Why one session per request instead of a global session?","A SQLAlchemy session holds transaction state and a connection and is not safe to share across concurrent requests. A per-request session isolates each request's transaction, commits or rolls back at the boundary, and returns its connection to the pool, which is both correct under concurrency and leak-free.",{"q":1173,"a":1174},"Should I call commit in the dependency or in the handler?","Committing once in the dependency after a successful yield is a clean default: the handler focuses on business logic and the dependency owns the transaction boundary. If a handler needs finer control over multiple transactions, it can manage session.begin() blocks itself, but the single commit-on-success pattern covers most routes.",{"q":1176,"a":1177},"How do I test code that uses the session dependency?","Override the dependency with one that yields a session bound to a test database — often an in-memory or transactional rollback session — and clear the override afterward. Because the session arrives by injection, no handler code changes between production and tests.",{"title":129,"description":1132},"MNjffUEoVMizlScpekqbACYB0WqArImfJfo-SIor9iU",[1181,1181],null,1781809863598]