[{"data":1,"prerenderedAt":2453},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F":580,"surround-\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F":2452},[4,186,386],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,42,66,90,114,150,174],{"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,30,36],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Before, After and Wrap Validators in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"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",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Cross-Field Validation Patterns in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"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",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[47,48,54,60],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"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",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Discriminated Unions in OpenAPI with Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Examples in the OpenAPI Schema with FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Excluding Fields Per Endpoint in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"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",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"Self-Referencing and Recursive Models in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[95,96,102,108],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"model_construct and When to Skip Validation in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002Findex",[101],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"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",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"TypeAdapter for Non-Model Types in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types\u002Findex",[113],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[119,120,126,132,138,144],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"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",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"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",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"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",[137],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Replacing json_encoders with field_serializer in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Migrating @root_validator to @model_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[155,156,162,168],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Optional vs Nullable Fields in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Query, Path and Body Parameter Validation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002Findex",[167],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Validating File Uploads and Forms in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[179,180],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"Annotated Dependencies and Reusable Types in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex",[185],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[191,194,224,254,284,308,338,362],{"title":192,"path":188,"stem":193},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":195,"path":196,"stem":197,"children":198},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[199,200,206,212,218],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"Concurrent Requests with asyncio.gather in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"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",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"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",[217],{"title":213,"path":214,"stem":215},{"title":219,"path":220,"stem":221,"children":222},"Running Sync Code in a Threadpool in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002Findex",[223],{"title":219,"path":220,"stem":221},{"title":225,"path":226,"stem":227,"children":228},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[229,230,236,242,248],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"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",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"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",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Testing with Async Database Fixtures in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002Findex",[247],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Transaction Management and Rollback in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[259,260,266,272,278],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"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",[265],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Retry and Idempotency for FastAPI Background Tasks","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks","async-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"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",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"When FastAPI BackgroundTasks Silently Fails","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails","async-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002Findex",[283],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[289,290,296,302],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"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",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[301],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"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",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[313,314,320,326,332],{"title":309,"path":310,"stem":311},{"title":315,"path":316,"stem":317,"children":318},"Correlating Logs, Traces and Errors in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors\u002Findex",[319],{"title":315,"path":316,"stem":317},{"title":321,"path":322,"stem":323,"children":324},"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",[325],{"title":321,"path":322,"stem":323},{"title":327,"path":328,"stem":329,"children":330},"Prometheus Metrics for FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi\u002Findex",[331],{"title":327,"path":328,"stem":329},{"title":333,"path":334,"stem":335,"children":336},"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",[337],{"title":333,"path":334,"stem":335},{"title":339,"path":340,"stem":341,"children":342},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[343,344,350,356],{"title":339,"path":340,"stem":341},{"title":345,"path":346,"stem":347,"children":348},"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",[349],{"title":345,"path":346,"stem":347},{"title":351,"path":352,"stem":353,"children":354},"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",[355],{"title":351,"path":352,"stem":353},{"title":357,"path":358,"stem":359,"children":360},"Rate Limit Headers and 429 Responses in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses","async-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses\u002Findex",[361],{"title":357,"path":358,"stem":359},{"title":363,"path":364,"stem":365,"children":366},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[367,368,374,380],{"title":363,"path":364,"stem":365},{"title":369,"path":370,"stem":371,"children":372},"Mocking External Services in FastAPI Tests","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002Findex",[373],{"title":369,"path":370,"stem":371},{"title":375,"path":376,"stem":377,"children":378},"TestClient vs httpx AsyncClient in FastAPI","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002Findex",[379],{"title":375,"path":376,"stem":377},{"title":381,"path":382,"stem":383,"children":384},"Testing Async FastAPI Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex",[385],{"title":381,"path":382,"stem":383},{"title":387,"path":388,"stem":389,"children":390},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[391,394,412,436,472,496,526,556],{"title":392,"path":388,"stem":393},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":395,"path":396,"stem":397,"children":398},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[399,400,406],{"title":395,"path":396,"stem":397},{"title":401,"path":402,"stem":403,"children":404},"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",[405],{"title":401,"path":402,"stem":403},{"title":407,"path":408,"stem":409,"children":410},"Lifespan Events vs Startup and Shutdown in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex",[411],{"title":407,"path":408,"stem":409},{"title":413,"path":414,"stem":415,"children":416},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[417,418,424,430],{"title":413,"path":414,"stem":415},{"title":419,"path":420,"stem":421,"children":422},"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",[423],{"title":419,"path":420,"stem":421},{"title":425,"path":426,"stem":427,"children":428},"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",[429],{"title":425,"path":426,"stem":427},{"title":431,"path":432,"stem":433,"children":434},"Secrets and .env Files Per Environment in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002Findex",[435],{"title":431,"path":432,"stem":433},{"title":437,"path":438,"stem":439,"children":440},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[441,442,448,454,460,466],{"title":437,"path":438,"stem":439},{"title":443,"path":444,"stem":445,"children":446},"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",[447],{"title":443,"path":444,"stem":445},{"title":449,"path":450,"stem":451,"children":452},"Dependency Caching and use_cache in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002Findex",[453],{"title":449,"path":450,"stem":451},{"title":455,"path":456,"stem":457,"children":458},"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",[459],{"title":455,"path":456,"stem":457},{"title":461,"path":462,"stem":463,"children":464},"Overriding Dependencies in FastAPI Tests","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002Findex",[465],{"title":461,"path":462,"stem":463},{"title":467,"path":468,"stem":469,"children":470},"Yield Dependencies and Cleanup Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex",[471],{"title":467,"path":468,"stem":469},{"title":473,"path":474,"stem":475,"children":476},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[477,478,484,490],{"title":473,"path":474,"stem":475},{"title":479,"path":480,"stem":481,"children":482},"Customising Validation Error Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002Findex",[483],{"title":479,"path":480,"stem":481},{"title":485,"path":486,"stem":487,"children":488},"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",[489],{"title":485,"path":486,"stem":487},{"title":491,"path":492,"stem":493,"children":494},"HTTPException vs Custom Exception Classes in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002Findex",[495],{"title":491,"path":492,"stem":493},{"title":497,"path":498,"stem":499,"children":500},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[501,502,508,514,520],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505,"children":506},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[507],{"title":503,"path":504,"stem":505},{"title":509,"path":510,"stem":511,"children":512},"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",[513],{"title":509,"path":510,"stem":511},{"title":515,"path":516,"stem":517,"children":518},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[519],{"title":515,"path":516,"stem":517},{"title":521,"path":522,"stem":523,"children":524},"Middleware vs Dependencies: When to Use Which","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex",[525],{"title":521,"path":522,"stem":523},{"title":527,"path":528,"stem":529,"children":530},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[531,532,538,544,550],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"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",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541,"children":542},"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",[543],{"title":539,"path":540,"stem":541},{"title":545,"path":546,"stem":547,"children":548},"Router Tags and OpenAPI Grouping in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping\u002Findex",[549],{"title":545,"path":546,"stem":547},{"title":551,"path":552,"stem":553,"children":554},"Versioning APIs with FastAPI Routers","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers\u002Findex",[555],{"title":551,"path":552,"stem":553},{"title":557,"path":558,"stem":559,"children":560},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[561,562,568,574],{"title":557,"path":558,"stem":559},{"title":563,"path":564,"stem":565,"children":566},"How a Request Flows Through FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex",[567],{"title":563,"path":564,"stem":565},{"title":569,"path":570,"stem":571,"children":572},"Response Model and Serialization Order","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002Findex",[573],{"title":569,"path":570,"stem":571},{"title":575,"path":576,"stem":577,"children":578},"Streaming and File Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002Findex",[579],{"title":575,"path":576,"stem":577},{"id":581,"title":467,"body":582,"dateModified":2422,"datePublished":2422,"description":2423,"extension":2424,"faq":2425,"howto":2436,"meta":2437,"navigation":901,"path":468,"seo":2449,"stem":469,"type":2450,"__hash__":2451},"content\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex.md",{"type":583,"value":584,"toc":2407},"minimark",[585,589,596,651,660,665,672,787,791,809,831,834,838,849,1660,1665,1672,1675,1689,1693,1699,1702,1719,1725,1731,1735,1741,1751,1755,1761,1770,1776,1903,1910,1914,1917,2038,2050,2054,2057,2228,2235,2239,2255,2261,2278,2287,2291,2297,2315,2329,2338,2344,2350,2354,2403],[586,587,467],"h1",{"id":588},"yield-dependencies-and-cleanup-order-in-fastapi",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,608,619,638,641],"ul",{},[600,601,602,603,607],"li",{},"Teardown is strictly LIFO: the dependency that set up last cleans up first, like nested ",[604,605,606],"code",{},"with"," blocks.",[600,609,610,611,614,615,618],{},"Every open ",[604,612,613],{},"yield"," dependency gets its ",[604,616,617],{},"finally"," block when the endpoint raises — that is what makes rollback-on-error reliable.",[600,620,621,622,624,625,628,629,632,633,637],{},"Wrapping the ",[604,623,613],{}," in ",[604,626,627],{},"try","\u002F",[604,630,631],{},"except"," lets a dependency ",[634,635,636],"em",{},"see"," the endpoint's exception, which is how commit-or-rollback is written.",[600,639,640],{},"When a dependency raises during setup, its own teardown is skipped but everything already open still unwinds.",[600,642,643,644,647,648,650],{},"On FastAPI 0.139.2, background tasks run ",[634,645,646],{},"before"," ",[604,649,613],{}," teardown. This ordering is version-specific; do not build on it.",[590,652,653,654,659],{},"This page is the lifecycle half of ",[655,656,658],"a",{"href":657},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","dependency injection strategies",". It answers, with real logs rather than assertion, exactly when your session closes.",[661,662,664],"h2",{"id":663},"the-problem-this-solves","The Problem This Solves",[590,666,667,668,671],{},"You have a session dependency that commits on success and rolls back on failure. Under load you see connections held longer than expected, or a rollback that did not happen, or a background task that explodes with ",[604,669,670],{},"session is closed",". All three are questions about ordering, and ordering is not something you can reason about from the FastAPI docs alone — the interaction between the dependency stack, the response, and the task queue is subtle enough that it deserves a measurement.",[673,674,675,783],"figure",{},[676,677,685,686,685,690,685,694,685,703,685,711,685,718,685,725,685,730,685,736,685,741,685,749,685,755,685,760,685,765,685,769,685,772,685,778],"svg",{"viewBox":678,"role":679,"ariaLabelledBy":680,"xmlns":683,"style":684},"0 0 720 340","img",[681,682],"yld-t","yld-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[687,688,689],"title",{"id":681},"LIFO teardown of nested yield dependencies",[691,692,693],"desc",{"id":682},"Setup runs outer then middle then inner, then the endpoint body executes, then teardown runs inner then middle then outer, in reverse order.",[695,696],"rect",{"x":697,"y":698,"width":699,"height":700,"rx":701,"style":702},"40","20","640","240","10","fill:none;stroke:currentColor;stroke-width:1.4px",[695,704],{"x":705,"y":706,"width":707,"height":708,"rx":709,"style":710},"70","46","580","188","9","fill:none;stroke:#00796B;stroke-width:1.6px",[712,713,717],"text",{"x":714,"y":715,"style":716},"94","66","text-anchor:start;fill:#00695C;font:600 12px sans-serif","outer",[695,719],{"x":720,"y":721,"width":722,"height":723,"rx":724,"style":710},"128","76","464","130","8",[712,726,729],{"x":727,"y":728,"style":716},"152","96","middle",[695,731],{"x":732,"y":733,"width":734,"height":735,"rx":724,"style":710},"186","106","348","72",[712,737,740],{"x":738,"y":739,"style":716},"210","126","inner",[695,742],{"x":743,"y":744,"width":745,"height":746,"rx":747,"style":748},"238","134","244","34","6","fill:#E0F2F1;stroke:#00796B;stroke-width:1.5px",[712,750,754],{"x":751,"y":752,"style":753},"360","156","text-anchor:middle;fill:#00695C;font:700 13px sans-serif","endpoint body",[712,756,759],{"x":723,"y":757,"style":758},"290","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","setup order",[712,761,764],{"x":723,"y":762,"style":763},"308","text-anchor:middle;fill:#4B5563;font:400 12px sans-serif","outer, middle, inner",[712,766,768],{"x":767,"y":757,"style":758},"560","teardown order",[712,770,771],{"x":767,"y":762,"style":763},"inner, middle, outer",[773,774],"line",{"x1":700,"y1":775,"x2":776,"y2":775,"style":777},"296","450","stroke:#00796B;stroke-width:1.6px",[779,780],"polygon",{"points":781,"style":782},"450,291 460,296 450,301","fill:#00796B",[784,785,786],"figcaption",{},"Each yield dependency is a scope. Scopes close in the reverse of the order they opened.",[661,788,790],{"id":789},"why-it-happens-an-asyncexitstack-per-request","Why It Happens: an AsyncExitStack per Request",[590,792,793,794,796,797,800,801,804,805,808],{},"A ",[604,795,613],{}," dependency is not special machinery. FastAPI wraps the generator in an async context manager and pushes it onto an ",[604,798,799],{},"AsyncExitStack"," that lives for the duration of the request. Setup is ",[604,802,803],{},"__aenter__","; teardown is ",[604,806,807],{},"__aexit__",".",[590,810,811,813,814,817,818,820,821,624,823,628,825,827,828,830],{},[604,812,799],{}," unwinds in reverse push order — that is its entire contract — which is where LIFO comes from. It is also where the exception behaviour comes from: when the stack is closed with an exception in flight, that exception is ",[634,815,816],{},"thrown into"," each suspended generator at its ",[604,819,613],{}," point. A generator that wraps the ",[604,822,613],{},[604,824,627],{},[604,826,631],{}," therefore sees a real Python exception object and can act on it, and a generator that only uses ",[604,829,617],{}," still gets its cleanup.",[590,832,833],{},"Because it is a stack and not a graph walk, sub-dependency relationships are preserved automatically: a provider cannot be torn down while something that was built on top of it is still tearing down.",[661,835,837],{"id":836},"proving-it-a-real-ordering-log","Proving It: a Real Ordering Log",[590,839,840,841,844,845,848],{},"Reasoning about this is fine; measuring it is better. This app appends to a module-level list at every interesting point and exposes a ",[604,842,843],{},"\u002Fevents"," endpoint that reports what the ",[634,846,847],{},"previous"," request logged. Because the log is written by real execution and read by a second real request, the sequences below are observations, not claims.",[850,851,856],"pre",{"className":852,"code":853,"language":854,"meta":855,"style":855},"language-python shiki shiki-themes github-light-high-contrast","\"\"\"yield dependencies: LIFO teardown, teardown on error, and ordering vs background tasks.\"\"\"\nfrom collections.abc import AsyncGenerator\nfrom typing import Annotated\n\nfrom fastapi import BackgroundTasks, Depends, FastAPI, HTTPException, Request\n\napp = FastAPI()\n\nEVENTS: list[str] = []\n\n\n@app.middleware(\"http\")\nasync def reset_events(request: Request, call_next):\n    if request.url.path != \"\u002Fevents\":\n        EVENTS.clear()                 # \u002Fevents reads the log the previous request left behind.\n    response = await call_next(request)\n    if request.url.path != \"\u002Fevents\":\n        EVENTS.append(\"middleware: got response from the route\")\n    return response\n\n\nasync def outer() -> AsyncGenerator[str, None]:\n    EVENTS.append(\"outer: setup\")\n    try:\n        yield \"outer\"\n    finally:\n        EVENTS.append(\"outer: teardown\")\n\n\nasync def middle(o: Annotated[str, Depends(outer)]) -> AsyncGenerator[str, None]:\n    EVENTS.append(\"middle: setup\")\n    try:\n        yield \"middle\"\n    finally:\n        EVENTS.append(\"middle: teardown\")\n\n\nasync def inner(m: Annotated[str, Depends(middle)]) -> AsyncGenerator[str, None]:\n    EVENTS.append(\"inner: setup\")\n    try:\n        yield \"inner\"\n    finally:\n        EVENTS.append(\"inner: teardown\")\n\n\nasync def observing_dep() -> AsyncGenerator[str, None]:\n    \"\"\"Sees whether the endpoint raised, exactly like a session that must roll back.\"\"\"\n    EVENTS.append(\"observing_dep: setup\")\n    try:\n        yield \"session\"\n    except HTTPException as exc:\n        EVENTS.append(f\"observing_dep: saw HTTPException {exc.status_code} -> rollback\")\n        raise\n    else:\n        EVENTS.append(\"observing_dep: no exception -> commit\")\n    finally:\n        EVENTS.append(\"observing_dep: closed\")\n\n\n@app.get(\"\u002Fok\")\nasync def ok(i: Annotated[str, Depends(inner)]):\n    EVENTS.append(\"endpoint: body ran\")\n    return {\"events_so_far\": list(EVENTS)}\n\n\n@app.get(\"\u002Fraises\")\nasync def raises(\n    i: Annotated[str, Depends(inner)],\n    s: Annotated[str, Depends(observing_dep)],\n):\n    EVENTS.append(\"endpoint: about to raise\")\n    raise HTTPException(status_code=409, detail=\"conflict\")\n","python","",[604,857,858,866,883,896,903,916,921,933,938,959,964,969,985,1000,1018,1031,1045,1058,1071,1080,1085,1090,1114,1127,1135,1144,1152,1164,1169,1174,1200,1212,1219,1227,1234,1246,1251,1256,1282,1294,1301,1309,1316,1328,1333,1338,1358,1364,1376,1383,1391,1406,1433,1439,1447,1459,1466,1478,1483,1488,1501,1519,1531,1555,1560,1565,1577,1590,1601,1612,1618,1630],{"__ignoreMap":855},[859,860,862],"span",{"class":773,"line":861},1,[859,863,865],{"class":864},"sYEJz","\"\"\"yield dependencies: LIFO teardown, teardown on error, and ordering vs background tasks.\"\"\"\n",[859,867,869,873,877,880],{"class":773,"line":868},2,[859,870,872],{"class":871},"sTJeM","from",[859,874,876],{"class":875},"sigWx"," collections.abc ",[859,878,879],{"class":871},"import",[859,881,882],{"class":875}," AsyncGenerator\n",[859,884,886,888,891,893],{"class":773,"line":885},3,[859,887,872],{"class":871},[859,889,890],{"class":875}," typing ",[859,892,879],{"class":871},[859,894,895],{"class":875}," Annotated\n",[859,897,899],{"class":773,"line":898},4,[859,900,902],{"emptyLinePlaceholder":901},true,"\n",[859,904,906,908,911,913],{"class":773,"line":905},5,[859,907,872],{"class":871},[859,909,910],{"class":875}," fastapi ",[859,912,879],{"class":871},[859,914,915],{"class":875}," BackgroundTasks, Depends, FastAPI, HTTPException, Request\n",[859,917,919],{"class":773,"line":918},6,[859,920,902],{"emptyLinePlaceholder":901},[859,922,924,927,930],{"class":773,"line":923},7,[859,925,926],{"class":875},"app ",[859,928,929],{"class":871},"=",[859,931,932],{"class":875}," FastAPI()\n",[859,934,936],{"class":773,"line":935},8,[859,937,902],{"emptyLinePlaceholder":901},[859,939,941,945,948,951,954,956],{"class":773,"line":940},9,[859,942,944],{"class":943},"sacAq","EVENTS",[859,946,947],{"class":875},": list[",[859,949,950],{"class":943},"str",[859,952,953],{"class":875},"] ",[859,955,929],{"class":871},[859,957,958],{"class":875}," []\n",[859,960,962],{"class":773,"line":961},10,[859,963,902],{"emptyLinePlaceholder":901},[859,965,967],{"class":773,"line":966},11,[859,968,902],{"emptyLinePlaceholder":901},[859,970,972,976,979,982],{"class":773,"line":971},12,[859,973,975],{"class":974},"s3dhs","@app.middleware",[859,977,978],{"class":875},"(",[859,980,981],{"class":864},"\"http\"",[859,983,984],{"class":875},")\n",[859,986,988,991,994,997],{"class":773,"line":987},13,[859,989,990],{"class":871},"async",[859,992,993],{"class":871}," def",[859,995,996],{"class":974}," reset_events",[859,998,999],{"class":875},"(request: Request, call_next):\n",[859,1001,1003,1006,1009,1012,1015],{"class":773,"line":1002},14,[859,1004,1005],{"class":871},"    if",[859,1007,1008],{"class":875}," request.url.path ",[859,1010,1011],{"class":871},"!=",[859,1013,1014],{"class":864}," \"\u002Fevents\"",[859,1016,1017],{"class":875},":\n",[859,1019,1021,1024,1027],{"class":773,"line":1020},15,[859,1022,1023],{"class":943},"        EVENTS",[859,1025,1026],{"class":875},".clear()                 ",[859,1028,1030],{"class":1029},"sFeEa","# \u002Fevents reads the log the previous request left behind.\n",[859,1032,1034,1037,1039,1042],{"class":773,"line":1033},16,[859,1035,1036],{"class":875},"    response ",[859,1038,929],{"class":871},[859,1040,1041],{"class":871}," await",[859,1043,1044],{"class":875}," call_next(request)\n",[859,1046,1048,1050,1052,1054,1056],{"class":773,"line":1047},17,[859,1049,1005],{"class":871},[859,1051,1008],{"class":875},[859,1053,1011],{"class":871},[859,1055,1014],{"class":864},[859,1057,1017],{"class":875},[859,1059,1061,1063,1066,1069],{"class":773,"line":1060},18,[859,1062,1023],{"class":943},[859,1064,1065],{"class":875},".append(",[859,1067,1068],{"class":864},"\"middleware: got response from the route\"",[859,1070,984],{"class":875},[859,1072,1074,1077],{"class":773,"line":1073},19,[859,1075,1076],{"class":871},"    return",[859,1078,1079],{"class":875}," response\n",[859,1081,1083],{"class":773,"line":1082},20,[859,1084,902],{"emptyLinePlaceholder":901},[859,1086,1088],{"class":773,"line":1087},21,[859,1089,902],{"emptyLinePlaceholder":901},[859,1091,1093,1095,1097,1100,1103,1105,1108,1111],{"class":773,"line":1092},22,[859,1094,990],{"class":871},[859,1096,993],{"class":871},[859,1098,1099],{"class":974}," outer",[859,1101,1102],{"class":875},"() -> AsyncGenerator[",[859,1104,950],{"class":943},[859,1106,1107],{"class":875},", ",[859,1109,1110],{"class":943},"None",[859,1112,1113],{"class":875},"]:\n",[859,1115,1117,1120,1122,1125],{"class":773,"line":1116},23,[859,1118,1119],{"class":943},"    EVENTS",[859,1121,1065],{"class":875},[859,1123,1124],{"class":864},"\"outer: setup\"",[859,1126,984],{"class":875},[859,1128,1130,1133],{"class":773,"line":1129},24,[859,1131,1132],{"class":871},"    try",[859,1134,1017],{"class":875},[859,1136,1138,1141],{"class":773,"line":1137},25,[859,1139,1140],{"class":871},"        yield",[859,1142,1143],{"class":864}," \"outer\"\n",[859,1145,1147,1150],{"class":773,"line":1146},26,[859,1148,1149],{"class":871},"    finally",[859,1151,1017],{"class":875},[859,1153,1155,1157,1159,1162],{"class":773,"line":1154},27,[859,1156,1023],{"class":943},[859,1158,1065],{"class":875},[859,1160,1161],{"class":864},"\"outer: teardown\"",[859,1163,984],{"class":875},[859,1165,1167],{"class":773,"line":1166},28,[859,1168,902],{"emptyLinePlaceholder":901},[859,1170,1172],{"class":773,"line":1171},29,[859,1173,902],{"emptyLinePlaceholder":901},[859,1175,1177,1179,1181,1184,1187,1189,1192,1194,1196,1198],{"class":773,"line":1176},30,[859,1178,990],{"class":871},[859,1180,993],{"class":871},[859,1182,1183],{"class":974}," middle",[859,1185,1186],{"class":875},"(o: Annotated[",[859,1188,950],{"class":943},[859,1190,1191],{"class":875},", Depends(outer)]) -> AsyncGenerator[",[859,1193,950],{"class":943},[859,1195,1107],{"class":875},[859,1197,1110],{"class":943},[859,1199,1113],{"class":875},[859,1201,1203,1205,1207,1210],{"class":773,"line":1202},31,[859,1204,1119],{"class":943},[859,1206,1065],{"class":875},[859,1208,1209],{"class":864},"\"middle: setup\"",[859,1211,984],{"class":875},[859,1213,1215,1217],{"class":773,"line":1214},32,[859,1216,1132],{"class":871},[859,1218,1017],{"class":875},[859,1220,1222,1224],{"class":773,"line":1221},33,[859,1223,1140],{"class":871},[859,1225,1226],{"class":864}," \"middle\"\n",[859,1228,1230,1232],{"class":773,"line":1229},34,[859,1231,1149],{"class":871},[859,1233,1017],{"class":875},[859,1235,1237,1239,1241,1244],{"class":773,"line":1236},35,[859,1238,1023],{"class":943},[859,1240,1065],{"class":875},[859,1242,1243],{"class":864},"\"middle: teardown\"",[859,1245,984],{"class":875},[859,1247,1249],{"class":773,"line":1248},36,[859,1250,902],{"emptyLinePlaceholder":901},[859,1252,1254],{"class":773,"line":1253},37,[859,1255,902],{"emptyLinePlaceholder":901},[859,1257,1259,1261,1263,1266,1269,1271,1274,1276,1278,1280],{"class":773,"line":1258},38,[859,1260,990],{"class":871},[859,1262,993],{"class":871},[859,1264,1265],{"class":974}," inner",[859,1267,1268],{"class":875},"(m: Annotated[",[859,1270,950],{"class":943},[859,1272,1273],{"class":875},", Depends(middle)]) -> AsyncGenerator[",[859,1275,950],{"class":943},[859,1277,1107],{"class":875},[859,1279,1110],{"class":943},[859,1281,1113],{"class":875},[859,1283,1285,1287,1289,1292],{"class":773,"line":1284},39,[859,1286,1119],{"class":943},[859,1288,1065],{"class":875},[859,1290,1291],{"class":864},"\"inner: setup\"",[859,1293,984],{"class":875},[859,1295,1297,1299],{"class":773,"line":1296},40,[859,1298,1132],{"class":871},[859,1300,1017],{"class":875},[859,1302,1304,1306],{"class":773,"line":1303},41,[859,1305,1140],{"class":871},[859,1307,1308],{"class":864}," \"inner\"\n",[859,1310,1312,1314],{"class":773,"line":1311},42,[859,1313,1149],{"class":871},[859,1315,1017],{"class":875},[859,1317,1319,1321,1323,1326],{"class":773,"line":1318},43,[859,1320,1023],{"class":943},[859,1322,1065],{"class":875},[859,1324,1325],{"class":864},"\"inner: teardown\"",[859,1327,984],{"class":875},[859,1329,1331],{"class":773,"line":1330},44,[859,1332,902],{"emptyLinePlaceholder":901},[859,1334,1336],{"class":773,"line":1335},45,[859,1337,902],{"emptyLinePlaceholder":901},[859,1339,1341,1343,1345,1348,1350,1352,1354,1356],{"class":773,"line":1340},46,[859,1342,990],{"class":871},[859,1344,993],{"class":871},[859,1346,1347],{"class":974}," observing_dep",[859,1349,1102],{"class":875},[859,1351,950],{"class":943},[859,1353,1107],{"class":875},[859,1355,1110],{"class":943},[859,1357,1113],{"class":875},[859,1359,1361],{"class":773,"line":1360},47,[859,1362,1363],{"class":864},"    \"\"\"Sees whether the endpoint raised, exactly like a session that must roll back.\"\"\"\n",[859,1365,1367,1369,1371,1374],{"class":773,"line":1366},48,[859,1368,1119],{"class":943},[859,1370,1065],{"class":875},[859,1372,1373],{"class":864},"\"observing_dep: setup\"",[859,1375,984],{"class":875},[859,1377,1379,1381],{"class":773,"line":1378},49,[859,1380,1132],{"class":871},[859,1382,1017],{"class":875},[859,1384,1386,1388],{"class":773,"line":1385},50,[859,1387,1140],{"class":871},[859,1389,1390],{"class":864}," \"session\"\n",[859,1392,1394,1397,1400,1403],{"class":773,"line":1393},51,[859,1395,1396],{"class":871},"    except",[859,1398,1399],{"class":875}," HTTPException ",[859,1401,1402],{"class":871},"as",[859,1404,1405],{"class":875}," exc:\n",[859,1407,1409,1411,1413,1416,1419,1422,1425,1428,1431],{"class":773,"line":1408},52,[859,1410,1023],{"class":943},[859,1412,1065],{"class":875},[859,1414,1415],{"class":871},"f",[859,1417,1418],{"class":864},"\"observing_dep: saw HTTPException ",[859,1420,1421],{"class":871},"{",[859,1423,1424],{"class":875},"exc.status_code",[859,1426,1427],{"class":871},"}",[859,1429,1430],{"class":864}," -> rollback\"",[859,1432,984],{"class":875},[859,1434,1436],{"class":773,"line":1435},53,[859,1437,1438],{"class":871},"        raise\n",[859,1440,1442,1445],{"class":773,"line":1441},54,[859,1443,1444],{"class":871},"    else",[859,1446,1017],{"class":875},[859,1448,1450,1452,1454,1457],{"class":773,"line":1449},55,[859,1451,1023],{"class":943},[859,1453,1065],{"class":875},[859,1455,1456],{"class":864},"\"observing_dep: no exception -> commit\"",[859,1458,984],{"class":875},[859,1460,1462,1464],{"class":773,"line":1461},56,[859,1463,1149],{"class":871},[859,1465,1017],{"class":875},[859,1467,1469,1471,1473,1476],{"class":773,"line":1468},57,[859,1470,1023],{"class":943},[859,1472,1065],{"class":875},[859,1474,1475],{"class":864},"\"observing_dep: closed\"",[859,1477,984],{"class":875},[859,1479,1481],{"class":773,"line":1480},58,[859,1482,902],{"emptyLinePlaceholder":901},[859,1484,1486],{"class":773,"line":1485},59,[859,1487,902],{"emptyLinePlaceholder":901},[859,1489,1491,1494,1496,1499],{"class":773,"line":1490},60,[859,1492,1493],{"class":974},"@app.get",[859,1495,978],{"class":875},[859,1497,1498],{"class":864},"\"\u002Fok\"",[859,1500,984],{"class":875},[859,1502,1504,1506,1508,1511,1514,1516],{"class":773,"line":1503},61,[859,1505,990],{"class":871},[859,1507,993],{"class":871},[859,1509,1510],{"class":974}," ok",[859,1512,1513],{"class":875},"(i: Annotated[",[859,1515,950],{"class":943},[859,1517,1518],{"class":875},", Depends(inner)]):\n",[859,1520,1522,1524,1526,1529],{"class":773,"line":1521},62,[859,1523,1119],{"class":943},[859,1525,1065],{"class":875},[859,1527,1528],{"class":864},"\"endpoint: body ran\"",[859,1530,984],{"class":875},[859,1532,1534,1536,1539,1542,1545,1548,1550,1552],{"class":773,"line":1533},63,[859,1535,1076],{"class":871},[859,1537,1538],{"class":875}," {",[859,1540,1541],{"class":864},"\"events_so_far\"",[859,1543,1544],{"class":875},": ",[859,1546,1547],{"class":943},"list",[859,1549,978],{"class":875},[859,1551,944],{"class":943},[859,1553,1554],{"class":875},")}\n",[859,1556,1558],{"class":773,"line":1557},64,[859,1559,902],{"emptyLinePlaceholder":901},[859,1561,1563],{"class":773,"line":1562},65,[859,1564,902],{"emptyLinePlaceholder":901},[859,1566,1568,1570,1572,1575],{"class":773,"line":1567},66,[859,1569,1493],{"class":974},[859,1571,978],{"class":875},[859,1573,1574],{"class":864},"\"\u002Fraises\"",[859,1576,984],{"class":875},[859,1578,1580,1582,1584,1587],{"class":773,"line":1579},67,[859,1581,990],{"class":871},[859,1583,993],{"class":871},[859,1585,1586],{"class":974}," raises",[859,1588,1589],{"class":875},"(\n",[859,1591,1593,1596,1598],{"class":773,"line":1592},68,[859,1594,1595],{"class":875},"    i: Annotated[",[859,1597,950],{"class":943},[859,1599,1600],{"class":875},", Depends(inner)],\n",[859,1602,1604,1607,1609],{"class":773,"line":1603},69,[859,1605,1606],{"class":875},"    s: Annotated[",[859,1608,950],{"class":943},[859,1610,1611],{"class":875},", Depends(observing_dep)],\n",[859,1613,1615],{"class":773,"line":1614},70,[859,1616,1617],{"class":875},"):\n",[859,1619,1621,1623,1625,1628],{"class":773,"line":1620},71,[859,1622,1119],{"class":943},[859,1624,1065],{"class":875},[859,1626,1627],{"class":864},"\"endpoint: about to raise\"",[859,1629,984],{"class":875},[859,1631,1633,1636,1639,1643,1645,1648,1650,1653,1655,1658],{"class":773,"line":1632},72,[859,1634,1635],{"class":871},"    raise",[859,1637,1638],{"class":875}," HTTPException(",[859,1640,1642],{"class":1641},"sV4o_","status_code",[859,1644,929],{"class":871},[859,1646,1647],{"class":943},"409",[859,1649,1107],{"class":875},[859,1651,1652],{"class":1641},"detail",[859,1654,929],{"class":871},[859,1656,1657],{"class":864},"\"conflict\"",[859,1659,984],{"class":875},[1661,1662,1664],"h3",{"id":1663},"the-happy-path-is-lifo","The happy path is LIFO",[850,1666,1670],{"className":1667,"code":1669,"language":712,"meta":855},[1668],"language-text","$ GET \u002Fok\n200 OK\n{\n  \"events_so_far\": [\n    \"outer: setup\",\n    \"middle: setup\",\n    \"inner: setup\",\n    \"endpoint: body ran\"\n  ]\n}\n\n$ GET \u002Fevents\n200 OK\n{\n  \"events\": [\n    \"outer: setup\",\n    \"middle: setup\",\n    \"inner: setup\",\n    \"endpoint: body ran\",\n    \"middleware: got response from the route\",\n    \"inner: teardown\",\n    \"middle: teardown\",\n    \"outer: teardown\"\n  ]\n}\n",[604,1671,1669],{"__ignoreMap":855},[590,1673,1674],{},"Setup is outer → middle → inner. Teardown is inner → middle → outer. LIFO, confirmed.",[590,1676,1677,1678,1681,1682,1684,1685,1688],{},"There is a second, less obvious fact in that transcript. ",[604,1679,1680],{},"middleware: got response from the route"," appears ",[634,1683,646],{}," any teardown. On the success path the teardown runs after the route has handed its response object outward. If your middleware measures request duration by timing ",[604,1686,1687],{},"call_next",", it is not counting the time your session takes to commit and close.",[1661,1690,1692],{"id":1691},"on-error-teardown-moves-earlier-and-sees-the-exception","On error, teardown moves earlier — and sees the exception",[850,1694,1697],{"className":1695,"code":1696,"language":712,"meta":855},[1668],"$ GET \u002Fraises\n409 Conflict\n{\n  \"detail\": \"conflict\"\n}\n\n$ GET \u002Fevents\n200 OK\n{\n  \"events\": [\n    \"outer: setup\",\n    \"middle: setup\",\n    \"inner: setup\",\n    \"observing_dep: setup\",\n    \"endpoint: about to raise\",\n    \"observing_dep: saw HTTPException 409 -> rollback\",\n    \"observing_dep: closed\",\n    \"inner: teardown\",\n    \"middle: teardown\",\n    \"outer: teardown\",\n    \"middleware: got response from the route\"\n  ]\n}\n",[604,1698,1696],{"__ignoreMap":855},[590,1700,1701],{},"Three things worth noticing.",[590,1703,1704,1707,1708,1711,1712,1715,1716,1718],{},[604,1705,1706],{},"observing_dep"," caught the real ",[604,1709,1710],{},"HTTPException"," raised by the endpoint. This is not a flag or an inspection of ",[604,1713,1714],{},"request.state"," — the exception is genuinely thrown into the generator at its ",[604,1717,613],{},", which is why the commit-or-rollback pattern works.",[590,1720,1721,1722,1724],{},"The teardown order is still LIFO, and ",[604,1723,1706],{}," — set up last — tears down first.",[590,1726,1727,1728,1730],{},"The whole unwind now happens ",[634,1729,646],{}," the middleware sees a response, the mirror image of the success case. The exception has to travel back through the dependency stack before an error response can be produced at all.",[1661,1732,1734],{"id":1733},"a-dependency-that-raises-during-setup","A dependency that raises during setup",[850,1736,1739],{"className":1737,"code":1738,"language":712,"meta":855},[1668],"$ GET \u002Fsetup-raises\n503 Service Unavailable\n{\n  \"detail\": \"dependency unavailable\"\n}\n\n$ GET \u002Fevents\n200 OK\n{\n  \"events\": [\n    \"failing_dep: setup\",\n    \"raises_during_setup: raising\",\n    \"failing_dep: teardown\",\n    \"middleware: got response from the route\"\n  ]\n}\n",[604,1740,1738],{"__ignoreMap":855},[590,1742,1743,1746,1747,1750],{},[604,1744,1745],{},"failing_dep"," had already yielded, so it is on the exit stack and gets torn down. ",[604,1748,1749],{},"raises_during_setup"," raised before producing a value, so there is nothing to unwind for it, and the endpoint body never runs. This is the correct behaviour for a dependency that gates the request — a failed auth check leaves no half-open resources behind.",[1661,1752,1754],{"id":1753},"background-tasks-run-before-teardown-on-01392","Background tasks run before teardown on 0.139.2",[850,1756,1759],{"className":1757,"code":1758,"language":712,"meta":855},[1668],"$ GET \u002Fwith-background\n200 OK\n{\n  \"scheduled\": true\n}\n\n$ GET \u002Fevents\n200 OK\n{\n  \"events\": [\n    \"task_dep: setup\",\n    \"endpoint: task scheduled\",\n    \"middleware: got response from the route\",\n    \"background task: ran\",\n    \"task_dep: teardown\"\n  ]\n}\n",[604,1760,1758],{"__ignoreMap":855},[590,1762,1763,1764,1766,1767,1769],{},"On FastAPI 0.139.2, the background task ran ",[634,1765,646],{}," the ",[604,1768,613],{}," dependency tore down. A session injected into the endpoint is therefore still open while the task executes.",[590,1771,1772,1773,1775],{},"Do not build on this. The relative position of ",[604,1774,613],{}," teardown and background tasks has moved between FastAPI releases, and code that quietly relies on a still-open session is code that breaks on an upgrade with an error far away from the cause. The durable pattern is for the task to acquire its own resources:",[850,1777,1779],{"className":852,"code":1778,"language":854,"meta":855,"style":855},"async def send_receipt(order_id: int) -> None:\n    # The task owns its session; it does not borrow the request's.\n    async with session_factory() as session:\n        order = await session.get(Order, order_id)\n        await mailer.send(order.email, render_receipt(order))\n\n\n@app.post(\"\u002Forders\u002F\")\nasync def create_order(order: OrderIn, tasks: BackgroundTasks, db: SessionDep):\n    saved = await repository.create(db, order)\n    tasks.add_task(send_receipt, saved.id)   # Pass an ID, never a live ORM object.\n    return saved\n",[604,1780,1781,1803,1808,1824,1836,1844,1848,1852,1864,1876,1888,1896],{"__ignoreMap":855},[859,1782,1783,1785,1787,1790,1793,1796,1799,1801],{"class":773,"line":861},[859,1784,990],{"class":871},[859,1786,993],{"class":871},[859,1788,1789],{"class":974}," send_receipt",[859,1791,1792],{"class":875},"(order_id: ",[859,1794,1795],{"class":943},"int",[859,1797,1798],{"class":875},") -> ",[859,1800,1110],{"class":943},[859,1802,1017],{"class":875},[859,1804,1805],{"class":773,"line":868},[859,1806,1807],{"class":1029},"    # The task owns its session; it does not borrow the request's.\n",[859,1809,1810,1813,1816,1819,1821],{"class":773,"line":885},[859,1811,1812],{"class":871},"    async",[859,1814,1815],{"class":871}," with",[859,1817,1818],{"class":875}," session_factory() ",[859,1820,1402],{"class":871},[859,1822,1823],{"class":875}," session:\n",[859,1825,1826,1829,1831,1833],{"class":773,"line":898},[859,1827,1828],{"class":875},"        order ",[859,1830,929],{"class":871},[859,1832,1041],{"class":871},[859,1834,1835],{"class":875}," session.get(Order, order_id)\n",[859,1837,1838,1841],{"class":773,"line":905},[859,1839,1840],{"class":871},"        await",[859,1842,1843],{"class":875}," mailer.send(order.email, render_receipt(order))\n",[859,1845,1846],{"class":773,"line":918},[859,1847,902],{"emptyLinePlaceholder":901},[859,1849,1850],{"class":773,"line":923},[859,1851,902],{"emptyLinePlaceholder":901},[859,1853,1854,1857,1859,1862],{"class":773,"line":935},[859,1855,1856],{"class":974},"@app.post",[859,1858,978],{"class":875},[859,1860,1861],{"class":864},"\"\u002Forders\u002F\"",[859,1863,984],{"class":875},[859,1865,1866,1868,1870,1873],{"class":773,"line":940},[859,1867,990],{"class":871},[859,1869,993],{"class":871},[859,1871,1872],{"class":974}," create_order",[859,1874,1875],{"class":875},"(order: OrderIn, tasks: BackgroundTasks, db: SessionDep):\n",[859,1877,1878,1881,1883,1885],{"class":773,"line":961},[859,1879,1880],{"class":875},"    saved ",[859,1882,929],{"class":871},[859,1884,1041],{"class":871},[859,1886,1887],{"class":875}," repository.create(db, order)\n",[859,1889,1890,1893],{"class":773,"line":966},[859,1891,1892],{"class":875},"    tasks.add_task(send_receipt, saved.id)   ",[859,1894,1895],{"class":1029},"# Pass an ID, never a live ORM object.\n",[859,1897,1898,1900],{"class":773,"line":971},[859,1899,1076],{"class":871},[859,1901,1902],{"class":875}," saved\n",[590,1904,1905,1906,808],{},"Passing an ID rather than a session or an ORM instance is the whole trick, and it is the same discipline described in ",[655,1907,1909],{"href":1908},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002F","when BackgroundTasks silently fails",[661,1911,1913],{"id":1912},"the-production-pattern","The Production Pattern",[590,1915,1916],{},"Putting the observed behaviour to work, a session dependency should look like this:",[850,1918,1920],{"className":852,"code":1919,"language":854,"meta":855,"style":855},"from collections.abc import AsyncGenerator\n\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\n\nasync def get_session() -> AsyncGenerator[AsyncSession, None]:\n    async with session_factory() as session:\n        try:\n            yield session\n            await session.commit()       # Reached only if the endpoint returned.\n        except Exception:\n            await session.rollback()     # The endpoint's exception arrives here.\n            raise                        # Re-raise: the error response still owes the client a body.\n",[604,1921,1922,1932,1936,1948,1952,1956,1972,1984,1991,1999,2010,2020,2030],{"__ignoreMap":855},[859,1923,1924,1926,1928,1930],{"class":773,"line":861},[859,1925,872],{"class":871},[859,1927,876],{"class":875},[859,1929,879],{"class":871},[859,1931,882],{"class":875},[859,1933,1934],{"class":773,"line":868},[859,1935,902],{"emptyLinePlaceholder":901},[859,1937,1938,1940,1943,1945],{"class":773,"line":885},[859,1939,872],{"class":871},[859,1941,1942],{"class":875}," sqlalchemy.ext.asyncio ",[859,1944,879],{"class":871},[859,1946,1947],{"class":875}," AsyncSession\n",[859,1949,1950],{"class":773,"line":898},[859,1951,902],{"emptyLinePlaceholder":901},[859,1953,1954],{"class":773,"line":905},[859,1955,902],{"emptyLinePlaceholder":901},[859,1957,1958,1960,1962,1965,1968,1970],{"class":773,"line":918},[859,1959,990],{"class":871},[859,1961,993],{"class":871},[859,1963,1964],{"class":974}," get_session",[859,1966,1967],{"class":875},"() -> AsyncGenerator[AsyncSession, ",[859,1969,1110],{"class":943},[859,1971,1113],{"class":875},[859,1973,1974,1976,1978,1980,1982],{"class":773,"line":923},[859,1975,1812],{"class":871},[859,1977,1815],{"class":871},[859,1979,1818],{"class":875},[859,1981,1402],{"class":871},[859,1983,1823],{"class":875},[859,1985,1986,1989],{"class":773,"line":935},[859,1987,1988],{"class":871},"        try",[859,1990,1017],{"class":875},[859,1992,1993,1996],{"class":773,"line":940},[859,1994,1995],{"class":871},"            yield",[859,1997,1998],{"class":875}," session\n",[859,2000,2001,2004,2007],{"class":773,"line":961},[859,2002,2003],{"class":871},"            await",[859,2005,2006],{"class":875}," session.commit()       ",[859,2008,2009],{"class":1029},"# Reached only if the endpoint returned.\n",[859,2011,2012,2015,2018],{"class":773,"line":966},[859,2013,2014],{"class":871},"        except",[859,2016,2017],{"class":943}," Exception",[859,2019,1017],{"class":875},[859,2021,2022,2024,2027],{"class":773,"line":971},[859,2023,2003],{"class":871},[859,2025,2026],{"class":875}," session.rollback()     ",[859,2028,2029],{"class":1029},"# The endpoint's exception arrives here.\n",[859,2031,2032,2035],{"class":773,"line":987},[859,2033,2034],{"class":871},"            raise",[859,2036,2037],{"class":1029},"                        # Re-raise: the error response still owes the client a body.\n",[590,2039,2040,2041,2044,2045,2049],{},"The ",[604,2042,2043],{},"raise"," is not optional. Swallowing the exception leaves FastAPI with a dependency that completed normally but an endpoint that never produced a return value, and the resulting behaviour is much harder to debug than the original error. The same reasoning applies to the ",[655,2046,2048],{"href":2047},"\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002F","global exception handlers"," sitting further out: they can only shape an error they actually receive.",[661,2051,2053],{"id":2052},"verification","Verification",[590,2055,2056],{},"Reproduce the ordering in your own app with a fixture that records:",[850,2058,2060],{"className":852,"code":2059,"language":854,"meta":855,"style":855},"@pytest.fixture\ndef order_log(app, monkeypatch):\n    log: list[str] = []\n\n    async def traced_session():\n        log.append(\"open\")\n        try:\n            yield FakeSession()\n        finally:\n            log.append(\"close\")\n\n    app.dependency_overrides[get_session] = traced_session\n    yield log\n    app.dependency_overrides.clear()\n\n\ndef test_session_closes_after_error(client, order_log):\n    client.get(\"\u002Forders\u002Fdoes-not-exist\")\n    assert order_log == [\"open\", \"close\"]     # closed even though the route 404'd\n",[604,2061,2062,2067,2078,2091,2095,2107,2117,2123,2130,2137,2147,2151,2161,2169,2174,2178,2182,2192,2202],{"__ignoreMap":855},[859,2063,2064],{"class":773,"line":861},[859,2065,2066],{"class":974},"@pytest.fixture\n",[859,2068,2069,2072,2075],{"class":773,"line":868},[859,2070,2071],{"class":871},"def",[859,2073,2074],{"class":974}," order_log",[859,2076,2077],{"class":875},"(app, monkeypatch):\n",[859,2079,2080,2083,2085,2087,2089],{"class":773,"line":885},[859,2081,2082],{"class":875},"    log: list[",[859,2084,950],{"class":943},[859,2086,953],{"class":875},[859,2088,929],{"class":871},[859,2090,958],{"class":875},[859,2092,2093],{"class":773,"line":898},[859,2094,902],{"emptyLinePlaceholder":901},[859,2096,2097,2099,2101,2104],{"class":773,"line":905},[859,2098,1812],{"class":871},[859,2100,993],{"class":871},[859,2102,2103],{"class":974}," traced_session",[859,2105,2106],{"class":875},"():\n",[859,2108,2109,2112,2115],{"class":773,"line":918},[859,2110,2111],{"class":875},"        log.append(",[859,2113,2114],{"class":864},"\"open\"",[859,2116,984],{"class":875},[859,2118,2119,2121],{"class":773,"line":923},[859,2120,1988],{"class":871},[859,2122,1017],{"class":875},[859,2124,2125,2127],{"class":773,"line":935},[859,2126,1995],{"class":871},[859,2128,2129],{"class":875}," FakeSession()\n",[859,2131,2132,2135],{"class":773,"line":940},[859,2133,2134],{"class":871},"        finally",[859,2136,1017],{"class":875},[859,2138,2139,2142,2145],{"class":773,"line":961},[859,2140,2141],{"class":875},"            log.append(",[859,2143,2144],{"class":864},"\"close\"",[859,2146,984],{"class":875},[859,2148,2149],{"class":773,"line":966},[859,2150,902],{"emptyLinePlaceholder":901},[859,2152,2153,2156,2158],{"class":773,"line":971},[859,2154,2155],{"class":875},"    app.dependency_overrides[get_session] ",[859,2157,929],{"class":871},[859,2159,2160],{"class":875}," traced_session\n",[859,2162,2163,2166],{"class":773,"line":987},[859,2164,2165],{"class":871},"    yield",[859,2167,2168],{"class":875}," log\n",[859,2170,2171],{"class":773,"line":1002},[859,2172,2173],{"class":875},"    app.dependency_overrides.clear()\n",[859,2175,2176],{"class":773,"line":1020},[859,2177,902],{"emptyLinePlaceholder":901},[859,2179,2180],{"class":773,"line":1033},[859,2181,902],{"emptyLinePlaceholder":901},[859,2183,2184,2186,2189],{"class":773,"line":1047},[859,2185,2071],{"class":871},[859,2187,2188],{"class":974}," test_session_closes_after_error",[859,2190,2191],{"class":875},"(client, order_log):\n",[859,2193,2194,2197,2200],{"class":773,"line":1060},[859,2195,2196],{"class":875},"    client.get(",[859,2198,2199],{"class":864},"\"\u002Forders\u002Fdoes-not-exist\"",[859,2201,984],{"class":875},[859,2203,2204,2207,2210,2213,2216,2218,2220,2222,2225],{"class":773,"line":1073},[859,2205,2206],{"class":871},"    assert",[859,2208,2209],{"class":875}," order_log ",[859,2211,2212],{"class":871},"==",[859,2214,2215],{"class":875}," [",[859,2217,2114],{"class":864},[859,2219,1107],{"class":875},[859,2221,2144],{"class":864},[859,2223,2224],{"class":875},"]     ",[859,2226,2227],{"class":1029},"# closed even though the route 404'd\n",[590,2229,2230,2231,808],{},"The override mechanism used here has its own sharp edges, covered in ",[655,2232,2234],{"href":2233},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","overriding dependencies in tests",[661,2236,2238],{"id":2237},"trade-offs-and-when-not-to","Trade-Offs and When Not To",[590,2240,2241,2246,2247,2249,2250,2254],{},[593,2242,793,2243,2245],{},[604,2244,613],{}," dependency holds its resource for the whole request."," If the handler does a database read and then a slow external HTTP call, a session-scoped ",[604,2248,613],{}," dependency keeps a pooled connection checked out across the network wait. Under concurrency that is how pools get exhausted; the diagnosis is in ",[655,2251,2253],{"href":2252},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002F","fixing asyncpg pool exhaustion",". Narrowing the scope means acquiring the session inside the handler for the section that needs it, at the cost of losing the automatic teardown.",[590,2256,2257,2260],{},[593,2258,2259],{},"Teardown is not a place for slow work."," On the success path it runs after the route has produced its response but before the connection is released; a two-second cleanup is two seconds of held resources.",[590,2262,2263,2266,2267,2269,2270,2273,2274,808],{},[593,2264,2265],{},"Caching and teardown are coupled."," A ",[604,2268,613],{}," dependency resolved twice in one request — which happens if the same provider is reached through two distinct callable objects, or with ",[604,2271,2272],{},"use_cache=False"," — sets up twice and tears down twice. If that provider opens a transaction, you now have two. The mechanics of when that happens are in ",[655,2275,2277],{"href":2276},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002F","dependency caching and use_cache",[590,2279,2280,2283,2284,2286],{},[593,2281,2282],{},"Exceptions raised in teardown are the worst kind."," They surface after the endpoint has notionally succeeded and can mask the original error. Keep the code after ",[604,2285,613],{}," boring: close, release, log. Nothing that can fail in an interesting way.",[661,2288,2290],{"id":2289},"faq","FAQ",[590,2292,2293,2296],{},[593,2294,2295],{},"In what order do yield dependencies clean up?","\nIn reverse order of setup, last in first out, exactly like nested context managers. If outer sets up, then middle, then inner, teardown runs inner, then middle, then outer. This guarantees a dependency is still usable while anything that depends on it is tearing down.",[590,2298,2299,2302,2303,614,2305,2307,2308,624,2310,628,2312,2314],{},[593,2300,2301],{},"Does the teardown run when the endpoint raises?","\nYes. The exception propagates back through the generator stack, so every open ",[604,2304,613],{},[604,2306,617],{}," block. A dependency that wraps its ",[604,2309,613],{},[604,2311,627],{},[604,2313,631],{}," can also observe the exception and roll back a transaction before re-raising.",[590,2316,2317,2320,2321,628,2323,2325,2326,2328],{},[593,2318,2319],{},"Can I catch the endpoint's exception inside a yield dependency?","\nYou can observe it with ",[604,2322,627],{},[604,2324,631],{}," around the ",[604,2327,613],{},", which is how session rollback is implemented. Swallowing it without re-raising is a mistake: FastAPI has already committed to an error response path and suppressing the exception there produces confusing behaviour.",[590,2330,2331,2334,2335,2337],{},[593,2332,2333],{},"Do yield dependencies stay open for background tasks?","\nOn FastAPI 0.139.2 the measured order is that background tasks run before ",[604,2336,613],{}," teardown, so a session is still open. This ordering has changed across FastAPI releases, so treat it as version-specific and pass tasks their own resources rather than depending on it.",[590,2339,2340,2343],{},[593,2341,2342],{},"What happens if a dependency raises before it yields?","\nIts own teardown never runs because the generator never suspended, but every dependency that already set up before it does tear down. The request short-circuits and the endpoint never executes.",[590,2345,2346,2349],{},[593,2347,2348],{},"Why does teardown appear after my middleware timer on success but before it on failure?","\nOn the success path the response object travels outward first and the exit stack closes behind it. On the error path the exception must unwind the dependency stack before any response exists to send. Both orderings are visible in the transcripts above.",[661,2351,2353],{"id":2352},"related-reading","Related Reading",[597,2355,2356,2364,2373,2381,2395],{},[600,2357,2358,647,2361,808],{},[593,2359,2360],{},"Up to the section:",[655,2362,2363],{"href":657},"Dependency Injection Strategies",[600,2365,2366,647,2369,2372],{},[593,2367,2368],{},"How many teardowns you get:",[655,2370,2371],{"href":2276},"Dependency Caching and use_cache"," — one cache entry, one teardown.",[600,2374,2375,647,2378,808],{},[593,2376,2377],{},"Replacing a yield dependency safely:",[655,2379,2380],{"href":2233},"Overriding Dependencies in Tests",[600,2382,2383,647,2386,2390,2391,808],{},[593,2384,2385],{},"Where the resource actually lives:",[655,2387,2389],{"href":2388},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F","Async SQLAlchemy Session per Request"," and ",[655,2392,2394],{"href":2393},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002F","Transaction Management and Rollback",[600,2396,2397,647,2400,808],{},[593,2398,2399],{},"Tasks that outlive the request:",[655,2401,2402],{"href":1908},"When BackgroundTasks Silently Fails",[2404,2405,2406],"style",{},"html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}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":855,"searchDepth":868,"depth":868,"links":2408},[2409,2410,2411,2417,2418,2419,2420,2421],{"id":663,"depth":868,"text":664},{"id":789,"depth":868,"text":790},{"id":836,"depth":868,"text":837,"children":2412},[2413,2414,2415,2416],{"id":1663,"depth":885,"text":1664},{"id":1691,"depth":885,"text":1692},{"id":1733,"depth":885,"text":1734},{"id":1753,"depth":885,"text":1754},{"id":1912,"depth":868,"text":1913},{"id":2052,"depth":868,"text":2053},{"id":2237,"depth":868,"text":2238},{"id":2289,"depth":868,"text":2290},{"id":2352,"depth":868,"text":2353},"2026-07-20","How FastAPI runs yield dependency teardown in LIFO order, what changes when the endpoint raises, and where background tasks sit, proven with real logs.","md",[2426,2428,2430,2432,2434],{"q":2295,"a":2427},"In reverse order of setup, last in first out, exactly like nested context managers. If outer sets up, then middle, then inner, teardown runs inner, then middle, then outer. This guarantees a dependency is still usable while anything that depends on it is tearing down.",{"q":2301,"a":2429},"Yes. The exception propagates back through the generator stack, so every open yield dependency gets its finally block. A dependency that wraps its yield in try or except can also observe the exception and roll back a transaction before re-raising.",{"q":2319,"a":2431},"You can observe it with try or except around the yield, which is how session rollback is implemented. Swallowing it without re-raising is a mistake: FastAPI has already committed to an error response path and suppressing the exception there produces confusing behaviour.",{"q":2333,"a":2433},"On FastAPI 0.139.2 the measured order is that background tasks run before yield teardown, so a session is still open. This ordering has changed across FastAPI releases, so treat it as version-specific and pass tasks their own resources rather than depending on it.",{"q":2342,"a":2435},"Its own teardown never runs because the generator never suspended, but every dependency that already set up before it does tear down. The request short-circuits and the endpoint never executes.",null,{"slug":2438,"breadcrumb":2439},"yield-dependencies-and-cleanup-order",[2440,2442,2445,2446],{"label":2441,"path":628},"Home",{"label":2443,"path":2444},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":2363,"path":657},{"label":2447,"path":2448},"Yield Dependencies and Cleanup Order","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F",{"title":467,"description":2423},"article","Rq1itoU0AMtOzL2hvrWnpAOeHmOZPrdA3djUmzPlJjA",[2436,2436],1784588202739]