[{"data":1,"prerenderedAt":1060},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F":1058},[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":123,"body":312,"description":1027,"extension":1028,"meta":1029,"navigation":512,"path":124,"seo":1056,"stem":125,"__hash__":1057},"content\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Findex.md",{"type":313,"value":314,"toc":1019},"minimark",[315,319,323,342,466,471,479,650,654,657,827,834,838,845,849,852,947,951,984,988,1015],[316,317,123],"h1",{"id":318},"async-database-sessions-in-fastapi",[320,321,322],"p",{},"Async database sessions are how FastAPI talks to a database without blocking the event loop: a shared async engine owns the connection pool, and each request draws its own session from it through a yield dependency.",[320,324,325,326,331,332,336,337,341],{},"This topic is part of ",[327,328,330],"a",{"href":329},"\u002Fasync-background-tasks-observability\u002F","Async, Background Tasks and Observability"," and is where ",[327,333,335],{"href":334},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002F","async correctness"," most often succeeds or fails, since a synchronous driver is the most common loop-blocking mistake. It extends the session pattern from ",[327,338,340],{"href":339},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","Dependency Injection Strategies",".",[343,344,345,462],"figure",{},[346,347,355,356,355,360,355,364,355,373,355,380,355,383,355,387,355,390,355,394,355,402,355,408,355,413,355,417,355,423,355,429,355,436,355,441,355,443,355,446,355,450,355,453,355,458],"svg",{"viewBox":348,"role":349,"ariaLabelledBy":350,"xmlns":353,"style":354},"0 0 760 230","img",[351,352],"db-title","db-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;font-family:Inter,system-ui,sans-serif","\n  ",[357,358,359],"title",{"id":351},"A shared async pool serving per-request sessions",[361,362,363],"desc",{"id":352},"Three concurrent requests each draw a request-scoped session from a shared async engine pool created at startup. Each session commits or rolls back and returns its connection to the pool when the request ends.",[365,366],"rect",{"x":367,"y":368,"width":369,"height":370,"rx":371,"style":372},"40","34","150","38","8","fill:#E0F2F1;stroke:#009688;stroke-width:1.5px",[374,375,379],"text",{"x":376,"y":377,"style":378},"115","58","fill:#00695C;font-size:11.5px;text-anchor:middle","request A → session",[365,381],{"x":367,"y":382,"width":369,"height":370,"rx":371,"style":372},"96",[374,384,386],{"x":376,"y":385,"style":378},"120","request B → session",[365,388],{"x":367,"y":389,"width":369,"height":370,"rx":371,"style":372},"158",[374,391,393],{"x":376,"y":392,"style":378},"182","request C → session",[365,395],{"x":396,"y":397,"width":398,"height":399,"rx":400,"style":401},"300","74","170","82","10","fill:#FFFFFF;stroke:#009688;stroke-width:2px",[374,403,407],{"x":404,"y":405,"style":406},"385","104","fill:#00695C;font-size:12.5px;font-weight:700;text-anchor:middle","Async engine",[374,409,412],{"x":404,"y":410,"style":411},"124","fill:#6B7280;font-size:10.5px;text-anchor:middle","connection pool",[374,414,416],{"x":404,"y":415,"style":411},"140","created at startup",[365,418],{"x":419,"y":420,"width":369,"height":421,"rx":371,"style":422},"580","92","46","fill:#F9FAFB;stroke:#D1D5DB;stroke-width:1.5px",[374,424,428],{"x":425,"y":426,"style":427},"655","119","fill:#374151;font-size:11.5px;text-anchor:middle","Database",[430,431],"line",{"x1":432,"y1":433,"x2":434,"y2":382,"style":435},"190","53","298","stroke:#4DB6AC;stroke-width:1.5px",[437,438],"polygon",{"points":439,"style":440},"294,91 300,95 291,99","fill:#4DB6AC",[430,442],{"x1":432,"y1":376,"x2":434,"y2":376,"style":435},[437,444],{"points":445,"style":440},"298,111 306,115 298,119",[430,447],{"x1":432,"y1":448,"x2":434,"y2":449,"style":435},"177","134",[437,451],{"points":452,"style":440},"294,131 300,135 291,139",[430,454],{"x1":455,"y1":376,"x2":456,"y2":376,"style":457},"470","578","stroke:#00796B;stroke-width:1.8px",[437,459],{"points":460,"style":461},"578,111 586,115 578,119","fill:#00796B",[463,464,465],"figcaption",{},"One engine and pool are created at startup; each concurrent request borrows a session, runs its transaction, and returns the connection to the pool.",[467,468,470],"h2",{"id":469},"core-mechanics-engine-in-lifespan-session-per-request","Core Mechanics: Engine in Lifespan, Session per Request",[320,472,473,474,478],{},"The engine and its pool are long-lived and belong in the ",[327,475,477],{"href":476},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002F","lifespan",". Sessions are per-request and belong in a yield dependency, so each request gets an isolated transaction that always cleans up.",[480,481,486],"pre",{"className":482,"code":483,"language":484,"meta":485,"style":485},"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    app.state.session_factory = async_sessionmaker(engine, expire_on_commit=False)\n    yield\n    await engine.dispose()   # Release the pool on shutdown.\n","python","",[487,488,489,507,514,527,540,545,550,557,572,584,610,631,637],"code",{"__ignoreMap":485},[490,491,493,497,501,504],"span",{"class":430,"line":492},1,[490,494,496],{"class":495},"sD7c4","from",[490,498,500],{"class":499},"sgsFI"," contextlib ",[490,502,503],{"class":495},"import",[490,505,506],{"class":499}," asynccontextmanager\n",[490,508,510],{"class":430,"line":509},2,[490,511,513],{"emptyLinePlaceholder":512},true,"\n",[490,515,517,519,522,524],{"class":430,"line":516},3,[490,518,496],{"class":495},[490,520,521],{"class":499}," fastapi ",[490,523,503],{"class":495},[490,525,526],{"class":499}," FastAPI\n",[490,528,530,532,535,537],{"class":430,"line":529},4,[490,531,496],{"class":495},[490,533,534],{"class":499}," sqlalchemy.ext.asyncio ",[490,536,503],{"class":495},[490,538,539],{"class":499}," async_sessionmaker, create_async_engine\n",[490,541,543],{"class":430,"line":542},5,[490,544,513],{"emptyLinePlaceholder":512},[490,546,548],{"class":430,"line":547},6,[490,549,513],{"emptyLinePlaceholder":512},[490,551,553],{"class":430,"line":552},7,[490,554,556],{"class":555},"s7eDp","@asynccontextmanager\n",[490,558,560,563,566,569],{"class":430,"line":559},8,[490,561,562],{"class":495},"async",[490,564,565],{"class":495}," def",[490,567,568],{"class":555}," lifespan",[490,570,571],{"class":499},"(app: FastAPI):\n",[490,573,575,578,581],{"class":430,"line":574},9,[490,576,577],{"class":499},"    engine ",[490,579,580],{"class":495},"=",[490,582,583],{"class":499}," create_async_engine(app.state.settings.database_url,\n",[490,585,587,591,593,596,599,602,604,607],{"class":430,"line":586},10,[490,588,590],{"class":589},"sqxcx","                                 pool_size",[490,592,580],{"class":495},[490,594,400],{"class":595},"sYu0t",[490,597,598],{"class":499},", ",[490,600,601],{"class":589},"max_overflow",[490,603,580],{"class":495},[490,605,606],{"class":595},"5",[490,608,609],{"class":499},")\n",[490,611,613,616,618,621,624,626,629],{"class":430,"line":612},11,[490,614,615],{"class":499},"    app.state.session_factory ",[490,617,580],{"class":495},[490,619,620],{"class":499}," async_sessionmaker(engine, ",[490,622,623],{"class":589},"expire_on_commit",[490,625,580],{"class":495},[490,627,628],{"class":595},"False",[490,630,609],{"class":499},[490,632,634],{"class":430,"line":633},12,[490,635,636],{"class":495},"    yield\n",[490,638,640,643,646],{"class":430,"line":639},13,[490,641,642],{"class":495},"    await",[490,644,645],{"class":499}," engine.dispose()   ",[490,647,649],{"class":648},"sAwPA","# Release the pool on shutdown.\n",[467,651,653],{"id":652},"production-implementation-the-session-dependency","Production Implementation: The Session Dependency",[320,655,656],{},"The yield dependency commits on success, rolls back on error, and always returns the connection — the contract that prevents leaks.",[480,658,660],{"className":482,"code":659,"language":484,"meta":485,"style":485},"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()\n        except Exception:\n            await session.rollback()   # Always runs on failure.\n            raise\n\n\nSessionDep = Annotated[AsyncSession, Depends(get_session)]\n",[487,661,662,674,686,690,701,712,716,720,738,755,763,771,779,789,800,806,811,816],{"__ignoreMap":485},[490,663,664,666,669,671],{"class":430,"line":492},[490,665,496],{"class":495},[490,667,668],{"class":499}," collections.abc ",[490,670,503],{"class":495},[490,672,673],{"class":499}," AsyncGenerator\n",[490,675,676,678,681,683],{"class":430,"line":509},[490,677,496],{"class":495},[490,679,680],{"class":499}," typing ",[490,682,503],{"class":495},[490,684,685],{"class":499}," Annotated\n",[490,687,688],{"class":430,"line":516},[490,689,513],{"emptyLinePlaceholder":512},[490,691,692,694,696,698],{"class":430,"line":529},[490,693,496],{"class":495},[490,695,521],{"class":499},[490,697,503],{"class":495},[490,699,700],{"class":499}," Depends, Request\n",[490,702,703,705,707,709],{"class":430,"line":542},[490,704,496],{"class":495},[490,706,534],{"class":499},[490,708,503],{"class":495},[490,710,711],{"class":499}," AsyncSession\n",[490,713,714],{"class":430,"line":547},[490,715,513],{"emptyLinePlaceholder":512},[490,717,718],{"class":430,"line":552},[490,719,513],{"emptyLinePlaceholder":512},[490,721,722,724,726,729,732,735],{"class":430,"line":559},[490,723,562],{"class":495},[490,725,565],{"class":495},[490,727,728],{"class":555}," get_session",[490,730,731],{"class":499},"(request: Request) -> AsyncGenerator[AsyncSession, ",[490,733,734],{"class":595},"None",[490,736,737],{"class":499},"]:\n",[490,739,740,743,746,749,752],{"class":430,"line":574},[490,741,742],{"class":495},"    async",[490,744,745],{"class":495}," with",[490,747,748],{"class":499}," request.app.state.session_factory() ",[490,750,751],{"class":495},"as",[490,753,754],{"class":499}," session:\n",[490,756,757,760],{"class":430,"line":586},[490,758,759],{"class":495},"        try",[490,761,762],{"class":499},":\n",[490,764,765,768],{"class":430,"line":612},[490,766,767],{"class":495},"            yield",[490,769,770],{"class":499}," session\n",[490,772,773,776],{"class":430,"line":633},[490,774,775],{"class":495},"            await",[490,777,778],{"class":499}," session.commit()\n",[490,780,781,784,787],{"class":430,"line":639},[490,782,783],{"class":495},"        except",[490,785,786],{"class":595}," Exception",[490,788,762],{"class":499},[490,790,792,794,797],{"class":430,"line":791},14,[490,793,775],{"class":495},[490,795,796],{"class":499}," session.rollback()   ",[490,798,799],{"class":648},"# Always runs on failure.\n",[490,801,803],{"class":430,"line":802},15,[490,804,805],{"class":495},"            raise\n",[490,807,809],{"class":430,"line":808},16,[490,810,513],{"emptyLinePlaceholder":512},[490,812,814],{"class":430,"line":813},17,[490,815,513],{"emptyLinePlaceholder":512},[490,817,819,822,824],{"class":430,"line":818},18,[490,820,821],{"class":499},"SessionDep ",[490,823,580],{"class":495},[490,825,826],{"class":499}," Annotated[AsyncSession, Depends(get_session)]\n",[320,828,829,830,341],{},"The deep dive on pool exhaustion and its fixes is in ",[327,831,833],{"href":832},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F","Fixing asyncpg Connection Pool Exhaustion",[467,835,837],{"id":836},"async-and-performance-notes","Async and Performance Notes",[320,839,840,841,844],{},"Every database call must be awaited so it yields the loop; a synchronous driver here blocks every request on the worker. Pool size sets your per-worker concurrency ceiling: ",[487,842,843],{},"pool_size + max_overflow"," is the most simultaneous in-flight queries one worker can hold, and across all workers that total must stay under the database's connection limit. Keep transactions short so connections return to the pool quickly.",[467,846,848],{"id":847},"testing-strategy","Testing Strategy",[320,850,851],{},"Use an isolated database per test and assert rollback on error:",[480,853,855],{"className":482,"code":854,"language":484,"meta":485,"style":485},"async def test_rollback_on_error(session):\n    with pytest.raises(ValueError):\n        async with session.begin():\n            await session.execute(insert_user(\"ada\"))\n            raise ValueError(\"boom\")          # Forces rollback.\n    assert await count_users(session) == 0    # Nothing persisted.\n",[487,856,857,869,883,893,907,927],{"__ignoreMap":485},[490,858,859,861,863,866],{"class":430,"line":492},[490,860,562],{"class":495},[490,862,565],{"class":495},[490,864,865],{"class":555}," test_rollback_on_error",[490,867,868],{"class":499},"(session):\n",[490,870,871,874,877,880],{"class":430,"line":509},[490,872,873],{"class":495},"    with",[490,875,876],{"class":499}," pytest.raises(",[490,878,879],{"class":595},"ValueError",[490,881,882],{"class":499},"):\n",[490,884,885,888,890],{"class":430,"line":516},[490,886,887],{"class":495},"        async",[490,889,745],{"class":495},[490,891,892],{"class":499}," session.begin():\n",[490,894,895,897,900,904],{"class":430,"line":529},[490,896,775],{"class":495},[490,898,899],{"class":499}," session.execute(insert_user(",[490,901,903],{"class":902},"sYBdl","\"ada\"",[490,905,906],{"class":499},"))\n",[490,908,909,912,915,918,921,924],{"class":430,"line":542},[490,910,911],{"class":495},"            raise",[490,913,914],{"class":595}," ValueError",[490,916,917],{"class":499},"(",[490,919,920],{"class":902},"\"boom\"",[490,922,923],{"class":499},")          ",[490,925,926],{"class":648},"# Forces rollback.\n",[490,928,929,932,935,938,941,944],{"class":430,"line":547},[490,930,931],{"class":495},"    assert",[490,933,934],{"class":495}," await",[490,936,937],{"class":499}," count_users(session) ",[490,939,940],{"class":495},"==",[490,942,943],{"class":595}," 0",[490,945,946],{"class":648},"    # Nothing persisted.\n",[467,948,950],{"id":949},"failure-modes-and-debugging","Failure Modes and Debugging",[952,953,954,962,968,978],"ul",{},[955,956,957,961],"li",{},[958,959,960],"strong",{},"Pool exhaustion."," Sessions not returned, or slow requests holding connections, drain the pool; use yield scoping and short transactions.",[955,963,964,967],{},[958,965,966],{},"Sync driver on the loop."," Blocks everything; use an async driver such as asyncpg.",[955,969,970,973,974,977],{},[958,971,972],{},"Oversized pools."," ",[487,975,976],{},"pool_size"," times workers exceeding the database limit causes connection refusals; size to fit.",[955,979,980,983],{},[958,981,982],{},"Shared sessions."," Reusing one session across requests corrupts transaction state; one session per request.",[467,985,987],{"id":986},"related-reading","Related Reading",[952,989,990,997,1004],{},[955,991,992,973,995,341],{},[958,993,994],{},"Up to the section:",[327,996,330],{"href":329},[955,998,999,973,1002,341],{},[958,1000,1001],{},"Hands-on guide:",[327,1003,833],{"href":832},[955,1005,1006,973,1009,1012,1013,341],{},[958,1007,1008],{},"Composes with:",[327,1010,1011],{"href":334},"Async Correctness and Concurrency"," and ",[327,1014,340],{"href":339},[1016,1017,1018],"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":485,"searchDepth":509,"depth":509,"links":1020},[1021,1022,1023,1024,1025,1026],{"id":469,"depth":509,"text":470},{"id":652,"depth":509,"text":653},{"id":836,"depth":509,"text":837},{"id":847,"depth":509,"text":848},{"id":949,"depth":509,"text":950},{"id":986,"depth":509,"text":987},"Manage async SQLAlchemy sessions in FastAPI: a shared async engine and pool, request-scoped sessions via yield dependencies, commit and rollback, pool sizing, and avoiding leaks.","md",{"slug":1030,"type":1031,"breadcrumb":1032,"datePublished":1041,"dateModified":1042,"faq":1043},"async-database-sessions","cluster",[1033,1036,1038],{"label":1034,"path":1035},"Home","\u002F",{"label":1037,"path":329},"Async, Background Tasks & Observability",{"label":1039,"path":1040},"Async Database Sessions","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002F","2026-02-14","2026-06-18",[1044,1047,1050,1053],{"q":1045,"a":1046},"Where should the async engine and session factory be created?","Create the async engine and its sessionmaker once at startup in the lifespan context manager, and store them on app.state. The engine owns the connection pool, which is a long-lived resource shared across all requests. Sessions are then created per request from the shared factory.",{"q":1048,"a":1049},"Why must each request get its own session?","A SQLAlchemy session is not safe to share across concurrent requests; it holds transaction state and a connection. A per-request session, provided through a yield dependency, isolates each request's transaction and is committed or rolled back and returned to the pool when the request ends.",{"q":1051,"a":1052},"How should I size the connection pool?","Size it to the concurrency you actually need per worker, not arbitrarily high. The total connections across all workers must stay under the database's connection limit, so pool_size times worker count plus overflow has to fit. An oversized pool exhausts the database; an undersized one queues requests.",{"q":1054,"a":1055},"What causes connection pool exhaustion in FastAPI?","Usually sessions that are never returned — a dependency that opens a session without a yield-based cleanup, a long-running request holding a connection while doing slow work, or more concurrent requests than the pool plus overflow can serve. The fix is yield-scoped sessions, short transactions, and correct sizing.",{"title":123,"description":1027},"zdhFsDEjYPYFze1vS6Ey1yo-Hp_Sbdj2n3OQBLHYj9I",[1059,1059],null,1781809863387]