[{"data":1,"prerenderedAt":2120},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002F":580,"surround-\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002F":2119},[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":407,"body":582,"dateModified":2089,"datePublished":2089,"description":2090,"extension":2091,"faq":2092,"howto":2103,"meta":2104,"navigation":931,"path":408,"seo":2116,"stem":409,"type":2117,"__hash__":2118},"content\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex.md",{"type":583,"value":584,"toc":2080},"minimark",[585,589,596,649,658,663,674,689,792,796,825,843,852,866,870,879,1421,1424,1611,1617,1708,1711,1718,1738,1752,1774,1778,1787,1862,1869,1880,1884,1890,1899,1914,1936,1949,1961,1965,1980,1997,2013,2032,2038,2042,2076],[586,587,407],"h1",{"id":588},"lifespan-events-vs-startup-and-shutdown-in-fastapi",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,611,618,632,643],"ul",{},[600,601,602,606,607,610],"li",{},[603,604,605],"code",{},"@app.on_event(\"startup\")"," is deprecated; the ",[603,608,609],{},"lifespan"," async context manager replaces both events.",[600,612,613,614,617],{},"Everything before ",[603,615,616],{},"yield"," finishes before the first request; everything after runs once traffic has stopped.",[600,619,620,621,624,625,628,629,631],{},"A ",[603,622,623],{},"try","\u002F",[603,626,627],{},"finally"," around the ",[603,630,616],{}," gives you reverse-order teardown for free.",[600,633,634,635,638,639,642],{},"Hold shared clients on ",[603,636,637],{},"app.state",", or yield a mapping that lands on ",[603,640,641],{},"request.state",".",[600,644,645,648],{},[603,646,647],{},"TestClient"," only runs the lifespan when used as a context manager.",[590,650,651,652,657],{},"This guide is part of ",[653,654,656],"a",{"href":655},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002F","Application Factory Patterns",", which covers building the app object; this page covers what happens to it either side of serving traffic.",[659,660,662],"h2",{"id":661},"the-problem-this-solves","The Problem This Solves",[590,664,665,666,669,670,673],{},"Your app needs one ",[603,667,668],{},"httpx.AsyncClient"," and one database pool, created once and shared by every request, then closed cleanly when the pod is drained. With ",[603,671,672],{},"@app.on_event"," this is spread across four decorated functions that communicate through module-level globals, no linter can tell you the shutdown handler closes something the startup handler created, and the whole thing is deprecated.",[590,675,676,677,680,681,684,685,688],{},"Worse, the split makes correct teardown accidental. If ",[603,678,679],{},"startup"," created three resources and the second one failed, ",[603,682,683],{},"shutdown"," still runs and tries to close all three, so you get an ",[603,686,687],{},"AttributeError"," in a shutdown handler masking the real failure.",[690,691,697,701,705,712,720,724,734,739,744,747,750,753,757,760,763,766,770,772,775,778,780,784,788],"svg",{"viewBox":692,"role":693,"ariaLabel":694,"xmlns":695,"style":696},"0 0 720 250","img","Lifespan timeline acquiring two clients, serving requests, then releasing in reverse order","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0",[698,699,700],"title",{},"The lifespan timeline",[702,703,704],"desc",{},"Five stages left to right: acquire the http client, acquire the database pool, yield and serve requests, close the database pool, close the http client — showing that resources are released in the reverse of the order they were acquired.",[706,707,711],"text",{"x":708,"y":709,"style":710},"360","26","text-anchor:middle;fill:#00796B;font:700 13px sans-serif","acquired first · released last",[713,714],"line",{"x1":715,"y1":716,"x2":717,"y2":716,"stroke":718,"strokeWidth":719},"76","40","630","#00796B","1.5",[721,722],"polygon",{"points":723,"fill":718},"630,35 642,40 630,45",[725,726],"rect",{"x":727,"y":728,"width":729,"height":730,"rx":731,"fill":732,"stroke":718,"strokeWidth":733},"11","70","130","72","8","none","2",[706,735,738],{"x":715,"y":736,"style":737},"100","text-anchor:middle;fill:#00796B;font:600 12px sans-serif","acquire",[706,740,743],{"x":715,"y":741,"style":742},"120","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","http client",[725,745],{"x":746,"y":728,"width":729,"height":730,"rx":731,"fill":732,"stroke":718,"strokeWidth":733},"153",[706,748,738],{"x":749,"y":736,"style":737},"218",[706,751,752],{"x":749,"y":741,"style":742},"db pool",[725,754],{"x":755,"y":728,"width":729,"height":730,"rx":731,"fill":732,"stroke":756,"strokeWidth":719},"295","currentColor",[706,758,616],{"x":708,"y":736,"style":759},"text-anchor:middle;fill:currentColor;font:600 12px sans-serif",[706,761,762],{"x":708,"y":741,"style":742},"serve requests",[725,764],{"x":765,"y":728,"width":729,"height":730,"rx":731,"fill":732,"stroke":756,"strokeWidth":719},"437",[706,767,769],{"x":768,"y":736,"style":759},"502","close",[706,771,752],{"x":768,"y":741,"style":742},[725,773],{"x":774,"y":728,"width":729,"height":730,"rx":731,"fill":732,"stroke":756,"strokeWidth":719},"579",[706,776,769],{"x":777,"y":736,"style":759},"644",[706,779,743],{"x":777,"y":741,"style":742},[706,781,783],{"x":708,"y":782,"style":742},"180","Before the yield: no request is accepted yet.",[706,785,787],{"x":708,"y":786,"style":742},"202","After the yield: no request is in flight any more.",[706,789,791],{"x":708,"y":790,"style":742},"228","One try\u002Ffinally block gives both guarantees.",[659,793,795],{"id":794},"why-it-happens","Why It Happens",[590,797,798,799,801,802,805,806,809,810,813,814,817,818,821,822,642],{},"The ASGI specification defines a ",[603,800,609],{}," scope alongside ",[603,803,804],{},"http"," and ",[603,807,808],{},"websocket",". Before binding a port, the server opens that scope and sends a ",[603,811,812],{},"lifespan.startup"," message; the application replies ",[603,815,816],{},"lifespan.startup.complete"," when it is ready. On shutdown the server sends ",[603,819,820],{},"lifespan.shutdown"," and waits for ",[603,823,824],{},"lifespan.shutdown.complete",[590,826,827,828,830,831,835,836,838,839,842],{},"Starlette implements that protocol by driving an async context manager. Your ",[603,829,609],{}," function ",[832,833,834],"em",{},"is"," that context manager: entering it corresponds to startup, the ",[603,837,616],{}," is the point at which the server is told it may accept connections, and exiting corresponds to shutdown. The ",[603,840,841],{},"on_event"," decorators were a compatibility layer over the same protocol — Starlette collected the registered callables into lists and awaited them in registration order on each side.",[590,844,845,846,848,849,851],{},"That difference in shape is where the ordering guarantee comes from. Two lists of callables give you order within each phase but no relationship between the phases: nothing connects the third startup handler to the third shutdown handler. A single function with a ",[603,847,616],{}," in the middle gives you Python's own scoping — the resource acquired on line three is still in scope after the yield, and a ",[603,850,627],{}," block runs whether the server stopped cleanly or the startup of a later resource blew up.",[590,853,854,855,858,859,861,862,865],{},"The yielded value matters too. Yield nothing and the context manager is purely for side effects. Yield a mapping and Starlette merges it into the ASGI scope's ",[603,856,857],{},"state",", which surfaces on ",[603,860,641],{}," in every handler. That is the mechanism behind the yielded ",[603,863,864],{},"pool"," in the example below.",[659,867,869],{"id":868},"the-fix","The Fix",[590,871,872,873,875,876,878],{},"One ",[603,874,609],{}," function, resources acquired in order, released in a ",[603,877,627],{}," in reverse. This example runs both styles in-process and records what actually happened:",[880,881,886],"pre",{"className":882,"code":883,"language":884,"meta":885,"style":885},"language-python shiki shiki-themes github-light-high-contrast","\"\"\"Compare @app.on_event ordering with the lifespan context manager, and record the real order.\"\"\"\nfrom contextlib import asynccontextmanager\nfrom typing import AsyncIterator\n\nfrom fastapi import FastAPI, Request\nfrom fastapi.testclient import TestClient\n\n\nclass FakeClient:\n    \"\"\"Stand-in for an httpx.AsyncClient or a database pool held for the app's lifetime.\"\"\"\n\n    def __init__(self, name: str) -> None:\n        self.name = name\n        self.closed = False\n\n    async def aclose(self) -> None:\n        self.closed = True\n        log(f\"closed {self.name}\")\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI) -> AsyncIterator[dict]:\n    log(\"lifespan: acquiring http client\")\n    http = FakeClient(\"http client\")\n    log(\"lifespan: acquiring db pool\")\n    pool = FakeClient(\"db pool\")\n    app.state.http = http\n    try:\n        yield {\"pool\": pool}          # yielded mapping is merged into request.state\n    finally:\n        # Reverse order: release what was acquired last, first.\n        log(\"lifespan: releasing\")\n        await pool.aclose()\n        await http.aclose()\n\n\nmodern = FastAPI(lifespan=lifespan)\n\n\n@modern.get(\"\u002Fping\")\nasync def modern_ping(request: Request) -> dict[str, object]:\n    log(\"lifespan app: request served\")\n    return {\n        \"style\": \"lifespan\",\n        \"http_client_from_app_state\": request.app.state.http.name,\n        \"pool_from_request_state\": request.state.pool.name,\n        \"pool_closed_during_request\": request.state.pool.closed,\n    }\n","python","",[603,887,888,896,913,926,933,946,959,964,969,982,988,993,1017,1032,1045,1050,1070,1082,1112,1117,1122,1128,1148,1159,1175,1185,1200,1211,1219,1238,1246,1252,1262,1271,1279,1284,1289,1307,1312,1317,1331,1354,1364,1373,1388,1397,1406,1415],{"__ignoreMap":885},[889,890,892],"span",{"class":713,"line":891},1,[889,893,895],{"class":894},"sYEJz","\"\"\"Compare @app.on_event ordering with the lifespan context manager, and record the real order.\"\"\"\n",[889,897,899,903,907,910],{"class":713,"line":898},2,[889,900,902],{"class":901},"sTJeM","from",[889,904,906],{"class":905},"sigWx"," contextlib ",[889,908,909],{"class":901},"import",[889,911,912],{"class":905}," asynccontextmanager\n",[889,914,916,918,921,923],{"class":713,"line":915},3,[889,917,902],{"class":901},[889,919,920],{"class":905}," typing ",[889,922,909],{"class":901},[889,924,925],{"class":905}," AsyncIterator\n",[889,927,929],{"class":713,"line":928},4,[889,930,932],{"emptyLinePlaceholder":931},true,"\n",[889,934,936,938,941,943],{"class":713,"line":935},5,[889,937,902],{"class":901},[889,939,940],{"class":905}," fastapi ",[889,942,909],{"class":901},[889,944,945],{"class":905}," FastAPI, Request\n",[889,947,949,951,954,956],{"class":713,"line":948},6,[889,950,902],{"class":901},[889,952,953],{"class":905}," fastapi.testclient ",[889,955,909],{"class":901},[889,957,958],{"class":905}," TestClient\n",[889,960,962],{"class":713,"line":961},7,[889,963,932],{"emptyLinePlaceholder":931},[889,965,967],{"class":713,"line":966},8,[889,968,932],{"emptyLinePlaceholder":931},[889,970,972,975,979],{"class":713,"line":971},9,[889,973,974],{"class":901},"class",[889,976,978],{"class":977},"sV4o_"," FakeClient",[889,980,981],{"class":905},":\n",[889,983,985],{"class":713,"line":984},10,[889,986,987],{"class":894},"    \"\"\"Stand-in for an httpx.AsyncClient or a database pool held for the app's lifetime.\"\"\"\n",[889,989,991],{"class":713,"line":990},11,[889,992,932],{"emptyLinePlaceholder":931},[889,994,996,999,1003,1006,1009,1012,1015],{"class":713,"line":995},12,[889,997,998],{"class":901},"    def",[889,1000,1002],{"class":1001},"sacAq"," __init__",[889,1004,1005],{"class":905},"(self, name: ",[889,1007,1008],{"class":1001},"str",[889,1010,1011],{"class":905},") -> ",[889,1013,1014],{"class":1001},"None",[889,1016,981],{"class":905},[889,1018,1020,1023,1026,1029],{"class":713,"line":1019},13,[889,1021,1022],{"class":1001},"        self",[889,1024,1025],{"class":905},".name ",[889,1027,1028],{"class":901},"=",[889,1030,1031],{"class":905}," name\n",[889,1033,1035,1037,1040,1042],{"class":713,"line":1034},14,[889,1036,1022],{"class":1001},[889,1038,1039],{"class":905},".closed ",[889,1041,1028],{"class":901},[889,1043,1044],{"class":1001}," False\n",[889,1046,1048],{"class":713,"line":1047},15,[889,1049,932],{"emptyLinePlaceholder":931},[889,1051,1053,1056,1059,1063,1066,1068],{"class":713,"line":1052},16,[889,1054,1055],{"class":901},"    async",[889,1057,1058],{"class":901}," def",[889,1060,1062],{"class":1061},"s3dhs"," aclose",[889,1064,1065],{"class":905},"(self) -> ",[889,1067,1014],{"class":1001},[889,1069,981],{"class":905},[889,1071,1073,1075,1077,1079],{"class":713,"line":1072},17,[889,1074,1022],{"class":1001},[889,1076,1039],{"class":905},[889,1078,1028],{"class":901},[889,1080,1081],{"class":1001}," True\n",[889,1083,1085,1088,1091,1094,1097,1100,1103,1106,1109],{"class":713,"line":1084},18,[889,1086,1087],{"class":905},"        log(",[889,1089,1090],{"class":901},"f",[889,1092,1093],{"class":894},"\"closed ",[889,1095,1096],{"class":901},"{",[889,1098,1099],{"class":1001},"self",[889,1101,1102],{"class":905},".name",[889,1104,1105],{"class":901},"}",[889,1107,1108],{"class":894},"\"",[889,1110,1111],{"class":905},")\n",[889,1113,1115],{"class":713,"line":1114},19,[889,1116,932],{"emptyLinePlaceholder":931},[889,1118,1120],{"class":713,"line":1119},20,[889,1121,932],{"emptyLinePlaceholder":931},[889,1123,1125],{"class":713,"line":1124},21,[889,1126,1127],{"class":1061},"@asynccontextmanager\n",[889,1129,1131,1134,1136,1139,1142,1145],{"class":713,"line":1130},22,[889,1132,1133],{"class":901},"async",[889,1135,1058],{"class":901},[889,1137,1138],{"class":1061}," lifespan",[889,1140,1141],{"class":905},"(app: FastAPI) -> AsyncIterator[",[889,1143,1144],{"class":1001},"dict",[889,1146,1147],{"class":905},"]:\n",[889,1149,1151,1154,1157],{"class":713,"line":1150},23,[889,1152,1153],{"class":905},"    log(",[889,1155,1156],{"class":894},"\"lifespan: acquiring http client\"",[889,1158,1111],{"class":905},[889,1160,1162,1165,1167,1170,1173],{"class":713,"line":1161},24,[889,1163,1164],{"class":905},"    http ",[889,1166,1028],{"class":901},[889,1168,1169],{"class":905}," FakeClient(",[889,1171,1172],{"class":894},"\"http client\"",[889,1174,1111],{"class":905},[889,1176,1178,1180,1183],{"class":713,"line":1177},25,[889,1179,1153],{"class":905},[889,1181,1182],{"class":894},"\"lifespan: acquiring db pool\"",[889,1184,1111],{"class":905},[889,1186,1188,1191,1193,1195,1198],{"class":713,"line":1187},26,[889,1189,1190],{"class":905},"    pool ",[889,1192,1028],{"class":901},[889,1194,1169],{"class":905},[889,1196,1197],{"class":894},"\"db pool\"",[889,1199,1111],{"class":905},[889,1201,1203,1206,1208],{"class":713,"line":1202},27,[889,1204,1205],{"class":905},"    app.state.http ",[889,1207,1028],{"class":901},[889,1209,1210],{"class":905}," http\n",[889,1212,1214,1217],{"class":713,"line":1213},28,[889,1215,1216],{"class":901},"    try",[889,1218,981],{"class":905},[889,1220,1222,1225,1228,1231,1234],{"class":713,"line":1221},29,[889,1223,1224],{"class":901},"        yield",[889,1226,1227],{"class":905}," {",[889,1229,1230],{"class":894},"\"pool\"",[889,1232,1233],{"class":905},": pool}          ",[889,1235,1237],{"class":1236},"sFeEa","# yielded mapping is merged into request.state\n",[889,1239,1241,1244],{"class":713,"line":1240},30,[889,1242,1243],{"class":901},"    finally",[889,1245,981],{"class":905},[889,1247,1249],{"class":713,"line":1248},31,[889,1250,1251],{"class":1236},"        # Reverse order: release what was acquired last, first.\n",[889,1253,1255,1257,1260],{"class":713,"line":1254},32,[889,1256,1087],{"class":905},[889,1258,1259],{"class":894},"\"lifespan: releasing\"",[889,1261,1111],{"class":905},[889,1263,1265,1268],{"class":713,"line":1264},33,[889,1266,1267],{"class":901},"        await",[889,1269,1270],{"class":905}," pool.aclose()\n",[889,1272,1274,1276],{"class":713,"line":1273},34,[889,1275,1267],{"class":901},[889,1277,1278],{"class":905}," http.aclose()\n",[889,1280,1282],{"class":713,"line":1281},35,[889,1283,932],{"emptyLinePlaceholder":931},[889,1285,1287],{"class":713,"line":1286},36,[889,1288,932],{"emptyLinePlaceholder":931},[889,1290,1292,1295,1297,1300,1302,1304],{"class":713,"line":1291},37,[889,1293,1294],{"class":905},"modern ",[889,1296,1028],{"class":901},[889,1298,1299],{"class":905}," FastAPI(",[889,1301,609],{"class":977},[889,1303,1028],{"class":901},[889,1305,1306],{"class":905},"lifespan)\n",[889,1308,1310],{"class":713,"line":1309},38,[889,1311,932],{"emptyLinePlaceholder":931},[889,1313,1315],{"class":713,"line":1314},39,[889,1316,932],{"emptyLinePlaceholder":931},[889,1318,1320,1323,1326,1329],{"class":713,"line":1319},40,[889,1321,1322],{"class":1061},"@modern.get",[889,1324,1325],{"class":905},"(",[889,1327,1328],{"class":894},"\"\u002Fping\"",[889,1330,1111],{"class":905},[889,1332,1334,1336,1338,1341,1344,1346,1349,1352],{"class":713,"line":1333},41,[889,1335,1133],{"class":901},[889,1337,1058],{"class":901},[889,1339,1340],{"class":1061}," modern_ping",[889,1342,1343],{"class":905},"(request: Request) -> dict[",[889,1345,1008],{"class":1001},[889,1347,1348],{"class":905},", ",[889,1350,1351],{"class":1001},"object",[889,1353,1147],{"class":905},[889,1355,1357,1359,1362],{"class":713,"line":1356},42,[889,1358,1153],{"class":905},[889,1360,1361],{"class":894},"\"lifespan app: request served\"",[889,1363,1111],{"class":905},[889,1365,1367,1370],{"class":713,"line":1366},43,[889,1368,1369],{"class":901},"    return",[889,1371,1372],{"class":905}," {\n",[889,1374,1376,1379,1382,1385],{"class":713,"line":1375},44,[889,1377,1378],{"class":894},"        \"style\"",[889,1380,1381],{"class":905},": ",[889,1383,1384],{"class":894},"\"lifespan\"",[889,1386,1387],{"class":905},",\n",[889,1389,1391,1394],{"class":713,"line":1390},45,[889,1392,1393],{"class":894},"        \"http_client_from_app_state\"",[889,1395,1396],{"class":905},": request.app.state.http.name,\n",[889,1398,1400,1403],{"class":713,"line":1399},46,[889,1401,1402],{"class":894},"        \"pool_from_request_state\"",[889,1404,1405],{"class":905},": request.state.pool.name,\n",[889,1407,1409,1412],{"class":713,"line":1408},47,[889,1410,1411],{"class":894},"        \"pool_closed_during_request\"",[889,1413,1414],{"class":905},": request.state.pool.closed,\n",[889,1416,1418],{"class":713,"line":1417},48,[889,1419,1420],{"class":905},"    }\n",[590,1422,1423],{},"The deprecated equivalent, for comparison — four functions, no relationship between them:",[880,1425,1427],{"className":882,"code":1426,"language":884,"meta":885,"style":885},"legacy = FastAPI()\n\n\n@legacy.on_event(\"startup\")\nasync def legacy_startup_first() -> None:\n    log(\"on_event startup #1\")\n\n\n@legacy.on_event(\"startup\")\nasync def legacy_startup_second() -> None:\n    log(\"on_event startup #2\")\n\n\n@legacy.on_event(\"shutdown\")\nasync def legacy_shutdown_first() -> None:\n    log(\"on_event shutdown #1\")\n\n\n@legacy.on_event(\"shutdown\")\nasync def legacy_shutdown_second() -> None:\n    log(\"on_event shutdown #2\")\n",[603,1428,1429,1439,1443,1447,1459,1475,1484,1488,1492,1502,1517,1526,1530,1534,1545,1560,1569,1573,1577,1587,1602],{"__ignoreMap":885},[889,1430,1431,1434,1436],{"class":713,"line":891},[889,1432,1433],{"class":905},"legacy ",[889,1435,1028],{"class":901},[889,1437,1438],{"class":905}," FastAPI()\n",[889,1440,1441],{"class":713,"line":898},[889,1442,932],{"emptyLinePlaceholder":931},[889,1444,1445],{"class":713,"line":915},[889,1446,932],{"emptyLinePlaceholder":931},[889,1448,1449,1452,1454,1457],{"class":713,"line":928},[889,1450,1451],{"class":1061},"@legacy.on_event",[889,1453,1325],{"class":905},[889,1455,1456],{"class":894},"\"startup\"",[889,1458,1111],{"class":905},[889,1460,1461,1463,1465,1468,1471,1473],{"class":713,"line":935},[889,1462,1133],{"class":901},[889,1464,1058],{"class":901},[889,1466,1467],{"class":1061}," legacy_startup_first",[889,1469,1470],{"class":905},"() -> ",[889,1472,1014],{"class":1001},[889,1474,981],{"class":905},[889,1476,1477,1479,1482],{"class":713,"line":948},[889,1478,1153],{"class":905},[889,1480,1481],{"class":894},"\"on_event startup #1\"",[889,1483,1111],{"class":905},[889,1485,1486],{"class":713,"line":961},[889,1487,932],{"emptyLinePlaceholder":931},[889,1489,1490],{"class":713,"line":966},[889,1491,932],{"emptyLinePlaceholder":931},[889,1493,1494,1496,1498,1500],{"class":713,"line":971},[889,1495,1451],{"class":1061},[889,1497,1325],{"class":905},[889,1499,1456],{"class":894},[889,1501,1111],{"class":905},[889,1503,1504,1506,1508,1511,1513,1515],{"class":713,"line":984},[889,1505,1133],{"class":901},[889,1507,1058],{"class":901},[889,1509,1510],{"class":1061}," legacy_startup_second",[889,1512,1470],{"class":905},[889,1514,1014],{"class":1001},[889,1516,981],{"class":905},[889,1518,1519,1521,1524],{"class":713,"line":990},[889,1520,1153],{"class":905},[889,1522,1523],{"class":894},"\"on_event startup #2\"",[889,1525,1111],{"class":905},[889,1527,1528],{"class":713,"line":995},[889,1529,932],{"emptyLinePlaceholder":931},[889,1531,1532],{"class":713,"line":1019},[889,1533,932],{"emptyLinePlaceholder":931},[889,1535,1536,1538,1540,1543],{"class":713,"line":1034},[889,1537,1451],{"class":1061},[889,1539,1325],{"class":905},[889,1541,1542],{"class":894},"\"shutdown\"",[889,1544,1111],{"class":905},[889,1546,1547,1549,1551,1554,1556,1558],{"class":713,"line":1047},[889,1548,1133],{"class":901},[889,1550,1058],{"class":901},[889,1552,1553],{"class":1061}," legacy_shutdown_first",[889,1555,1470],{"class":905},[889,1557,1014],{"class":1001},[889,1559,981],{"class":905},[889,1561,1562,1564,1567],{"class":713,"line":1052},[889,1563,1153],{"class":905},[889,1565,1566],{"class":894},"\"on_event shutdown #1\"",[889,1568,1111],{"class":905},[889,1570,1571],{"class":713,"line":1072},[889,1572,932],{"emptyLinePlaceholder":931},[889,1574,1575],{"class":713,"line":1084},[889,1576,932],{"emptyLinePlaceholder":931},[889,1578,1579,1581,1583,1585],{"class":713,"line":1114},[889,1580,1451],{"class":1061},[889,1582,1325],{"class":905},[889,1584,1542],{"class":894},[889,1586,1111],{"class":905},[889,1588,1589,1591,1593,1596,1598,1600],{"class":713,"line":1119},[889,1590,1133],{"class":901},[889,1592,1058],{"class":901},[889,1594,1595],{"class":1061}," legacy_shutdown_second",[889,1597,1470],{"class":905},[889,1599,1014],{"class":1001},[889,1601,981],{"class":905},[889,1603,1604,1606,1609],{"class":713,"line":1124},[889,1605,1153],{"class":905},[889,1607,1608],{"class":894},"\"on_event shutdown #2\"",[889,1610,1111],{"class":905},[590,1612,1613,1614,1616],{},"Both applications were then driven through ",[603,1615,647],{}," as a context manager, so their real lifespans ran:",[880,1618,1620],{"className":882,"code":1619,"language":884,"meta":885,"style":885},"log(\"--- on_event app ---\")\nwith TestClient(legacy) as client:\n    client.get(\"\u002Fping\")\nlog(\"--- lifespan app ---\")\nwith TestClient(modern) as client:\n    client.get(\"\u002Fping\")\n    RESPONSE = client.get(\"\u002Fping\").json()\nlog(\"--- both apps stopped ---\")\n",[603,1621,1622,1632,1646,1655,1664,1675,1683,1699],{"__ignoreMap":885},[889,1623,1624,1627,1630],{"class":713,"line":891},[889,1625,1626],{"class":905},"log(",[889,1628,1629],{"class":894},"\"--- on_event app ---\"",[889,1631,1111],{"class":905},[889,1633,1634,1637,1640,1643],{"class":713,"line":898},[889,1635,1636],{"class":901},"with",[889,1638,1639],{"class":905}," TestClient(legacy) ",[889,1641,1642],{"class":901},"as",[889,1644,1645],{"class":905}," client:\n",[889,1647,1648,1651,1653],{"class":713,"line":915},[889,1649,1650],{"class":905},"    client.get(",[889,1652,1328],{"class":894},[889,1654,1111],{"class":905},[889,1656,1657,1659,1662],{"class":713,"line":928},[889,1658,1626],{"class":905},[889,1660,1661],{"class":894},"\"--- lifespan app ---\"",[889,1663,1111],{"class":905},[889,1665,1666,1668,1671,1673],{"class":713,"line":935},[889,1667,1636],{"class":901},[889,1669,1670],{"class":905}," TestClient(modern) ",[889,1672,1642],{"class":901},[889,1674,1645],{"class":905},[889,1676,1677,1679,1681],{"class":713,"line":948},[889,1678,1650],{"class":905},[889,1680,1328],{"class":894},[889,1682,1111],{"class":905},[889,1684,1685,1688,1691,1694,1696],{"class":713,"line":961},[889,1686,1687],{"class":1001},"    RESPONSE",[889,1689,1690],{"class":901}," =",[889,1692,1693],{"class":905}," client.get(",[889,1695,1328],{"class":894},[889,1697,1698],{"class":905},").json()\n",[889,1700,1701,1703,1706],{"class":713,"line":966},[889,1702,1626],{"class":905},[889,1704,1705],{"class":894},"\"--- both apps stopped ---\"",[889,1707,1111],{"class":905},[590,1709,1710],{},"The recorded order, from a real run:",[880,1712,1716],{"className":1713,"code":1715,"language":706,"meta":885},[1714],"language-text","$ GET \u002Forder\n200 OK\n{\n  \"events\": [\n    \"--- on_event app ---\",\n    \"on_event startup #1\",\n    \"on_event startup #2\",\n    \"on_event app: request served\",\n    \"on_event shutdown #1\",\n    \"on_event shutdown #2\",\n    \"--- lifespan app ---\",\n    \"lifespan: acquiring http client\",\n    \"lifespan: acquiring db pool\",\n    \"lifespan app: request served\",\n    \"lifespan app: request served\",\n    \"lifespan: releasing\",\n    \"closed db pool\",\n    \"closed http client\",\n    \"--- both apps stopped ---\"\n  ],\n  \"lifespan_app_response\": {\n    \"style\": \"lifespan\",\n    \"http_client_from_app_state\": \"http client\",\n    \"pool_from_request_state\": \"db pool\",\n    \"pool_closed_during_request\": false\n  }\n}\n",[603,1717,1715],{"__ignoreMap":885},[590,1719,1720,1721,1723,1724,1727,1728,1731,1732,1734,1735,1737],{},"Read the two blocks side by side. The ",[603,1722,841],{}," app runs its startup handlers in registration order and its shutdown handlers in registration order — ",[603,1725,1726],{},"#1"," then ",[603,1729,1730],{},"#2"," in both directions. Nothing is wrong with that, but nothing about it expresses that shutdown ",[603,1733,1726],{}," undoes startup ",[603,1736,1726],{}," either; you are relying on the reader to notice.",[590,1739,1740,1741,1744,1745,1748,1749,1751],{},"The lifespan block shows the shape you want. Both resources are acquired, requests are served — twice, on the same objects — and then teardown runs ",[603,1742,1743],{},"closed db pool"," before ",[603,1746,1747],{},"closed http client",", the reverse of acquisition. That reversal is not something the framework does for you; it is what a ",[603,1750,627],{}," block written in the obvious order produces.",[590,1753,1754,1755,1758,1759,1762,1763,1766,1767,1770,1771,1773],{},"The response body confirms both handles are reachable from a request. ",[603,1756,1757],{},"app.state.http"," is the client stored as an attribute; ",[603,1760,1761],{},"request.state.pool"," came from the dictionary the lifespan yielded. And ",[603,1764,1765],{},"pool_closed_during_request"," is ",[603,1768,1769],{},"false",", which is the assertion that matters: no request ever sees a closed handle, because the server stops accepting requests before the code after ",[603,1772,616],{}," runs.",[659,1775,1777],{"id":1776},"verification","Verification",[590,1779,1780,1781,1783,1784,1786],{},"The fastest check is the one that catches the most common mistake — a ",[603,1782,647],{}," without a ",[603,1785,1636],{}," block:",[880,1788,1790],{"className":882,"code":1789,"language":884,"meta":885,"style":885},"def test_lifespan_runs_and_cleans_up():\n    with TestClient(modern) as client:\n        assert client.get(\"\u002Fping\").json()[\"pool_closed_during_request\"] is False\n        pool = client.app.state.http\n    assert pool.closed is True          # closed by the time the block exits\n",[603,1791,1792,1803,1814,1836,1846],{"__ignoreMap":885},[889,1793,1794,1797,1800],{"class":713,"line":891},[889,1795,1796],{"class":901},"def",[889,1798,1799],{"class":1061}," test_lifespan_runs_and_cleans_up",[889,1801,1802],{"class":905},"():\n",[889,1804,1805,1808,1810,1812],{"class":713,"line":898},[889,1806,1807],{"class":901},"    with",[889,1809,1670],{"class":905},[889,1811,1642],{"class":901},[889,1813,1645],{"class":905},[889,1815,1816,1819,1821,1823,1826,1829,1832,1834],{"class":713,"line":915},[889,1817,1818],{"class":901},"        assert",[889,1820,1693],{"class":905},[889,1822,1328],{"class":894},[889,1824,1825],{"class":905},").json()[",[889,1827,1828],{"class":894},"\"pool_closed_during_request\"",[889,1830,1831],{"class":905},"] ",[889,1833,834],{"class":901},[889,1835,1044],{"class":1001},[889,1837,1838,1841,1843],{"class":713,"line":928},[889,1839,1840],{"class":905},"        pool ",[889,1842,1028],{"class":901},[889,1844,1845],{"class":905}," client.app.state.http\n",[889,1847,1848,1851,1854,1856,1859],{"class":713,"line":935},[889,1849,1850],{"class":901},"    assert",[889,1852,1853],{"class":905}," pool.closed ",[889,1855,834],{"class":901},[889,1857,1858],{"class":1001}," True",[889,1860,1861],{"class":1236},"          # closed by the time the block exits\n",[590,1863,1864,1865,1868],{},"If that first assertion raises ",[603,1866,1867],{},"AttributeError: state",", you have found a test constructing the client outside a context manager somewhere. It is worth a project-wide grep.",[590,1870,1871,1872,1875,1876,1879],{},"In a deployed service, verify from the log timeline. A correct lifespan produces a startup line, then the server's \"application startup complete\", then traffic, then your teardown lines ",[832,1873,1874],{},"before"," the process exits. If teardown lines appear after the server has already reported the port closed but the process is killed before they finish, your orchestrator's grace period is shorter than your teardown — raise ",[603,1877,1878],{},"terminationGracePeriodSeconds"," or make the teardown faster.",[659,1881,1883],{"id":1882},"trade-offs-and-when-not-to","Trade-offs and When Not To",[590,1885,1886,1889],{},[593,1887,1888],{},"Slow startup delays every deploy and every restart."," A lifespan that warms a large cache or runs migrations blocks the readiness of that replica. Prefer to open pools lazily where the library supports it, and move genuinely slow work to a job that runs before the rollout rather than inside it.",[590,1891,1892,1895,1896,1898],{},[593,1893,1894],{},"Startup failures should stop the deploy — usually."," An exception before ",[603,1897,616],{}," means the server never binds. That is right for a database. It is wrong for an optional dependency such as a feature-flag service, where a failure should degrade rather than block; catch that one explicitly and record the degraded state.",[590,1900,1901,1904,1905,1908,1909,1913],{},[593,1902,1903],{},"A mounted sub-application has its own lifespan."," Starlette does not propagate the parent's lifespan into an app attached with ",[603,1906,1907],{},"mount",", so a resource created in the parent is not created for the child. ",[653,1910,1912],{"href":1911},"\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002F","APIRouter Prefix vs Sub-Application Mounting"," covers what else stops at that boundary.",[590,1915,1916,1921,1922,1925,1926,1928,1929,1931,1932,1935],{},[593,1917,1918,1920],{},[603,1919,637],{}," is untyped."," It is a plain namespace, so ",[603,1923,1924],{},"request.app.state.htpp"," is a runtime ",[603,1927,687],{}," rather than a type error. If that bothers you — and at scale it should — expose each resource through a dependency that reads ",[603,1930,637],{}," once and returns a typed handle, which also makes it overridable in tests. ",[653,1933,443],{"href":1934},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002F"," covers that pattern.",[590,1937,1938,1941,1942,1945,1946,1948],{},[593,1939,1940],{},"Do not mix the two mechanisms."," Passing ",[603,1943,1944],{},"lifespan="," while also registering ",[603,1947,672],{}," handlers is not a supported combination; migrate all of them at once.",[590,1950,1951,1952,1955,1956,1960],{},"Per-request resources are a different problem entirely. A database ",[832,1953,1954],{},"session"," should be created and closed per request by a dependency, not held for the app's lifetime — see ",[653,1957,1959],{"href":1958},"\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002F","Async SQLAlchemy Session Per Request",". The lifespan owns the pool; the dependency owns the session.",[659,1962,1964],{"id":1963},"faq","FAQ",[590,1966,1967,1970,1971,1973,1974,1976,1977,1979],{},[593,1968,1969],{},"Is @app.on_event deprecated?","\nYes. FastAPI deprecated the ",[603,1972,841],{}," decorators in favour of the ",[603,1975,609],{}," argument, and Starlette has done the same. They still work, but new code should use ",[603,1978,609],{}," and existing code should migrate — the two mechanisms cannot be mixed on one application.",[590,1981,1982,1985,1986,1988,1989,1991,1992,624,1994,1996],{},[593,1983,1984],{},"What ordering does lifespan actually guarantee?","\nEverything before the ",[603,1987,616],{}," completes before the server accepts a single request, and everything after the ",[603,1990,616],{}," runs only once the server has stopped accepting them. Within the function, statements run in the order you wrote them, so teardown in reverse acquisition order is just the natural shape of a ",[603,1993,623],{},[603,1995,627],{}," block.",[590,1998,1999,2002,2003,2005,2006,2009,2010,2012],{},[593,2000,2001],{},"Should shared clients live on app.state or in the yielded dictionary?","\nUse ",[603,2004,637],{}," for things a background task, an exception handler or a startup check might also need, since it is reachable from ",[603,2007,2008],{},"request.app.state"," anywhere. Use the yielded mapping for values you only ever read inside a request, since it lands on ",[603,2011,641],{}," and is typed a little more naturally.",[590,2014,2015,2018,2019,2021,2022,2025,2026,2028,2029,2031],{},[593,2016,2017],{},"Does lifespan run under TestClient?","\nOnly when you use ",[603,2020,647],{}," as a context manager. A bare ",[603,2023,2024],{},"TestClient(app)"," skips the lifespan entirely, which is why tests suddenly fail with a missing attribute on ",[603,2027,637],{}," — wrap it in a ",[603,2030,1636],{}," block and startup runs.",[590,2033,2034,2037],{},[593,2035,2036],{},"What happens if startup raises an exception?","\nThe lifespan fails, the ASGI server logs the error and exits without binding the port, so the application never serves traffic in a half-initialised state. That is the desired behaviour: a database that is unreachable at boot should stop the deploy, not produce a pod that 500s on every request.",[659,2039,2041],{"id":2040},"related-reading","Related Reading",[597,2043,2044,2053,2059,2064,2071],{},[600,2045,2046,2049,2050,2052],{},[593,2047,2048],{},"Up to the topic:"," ",[653,2051,656],{"href":655},", where the lifespan is attached to the app.",[600,2054,2055,2056,642],{},"Factory and testing mechanics in full: ",[653,2057,401],{"href":2058},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002F",[600,2060,2061,2062,642],{},"Exposing lifespan-owned resources to handlers: ",[653,2063,443],{"href":1934},[600,2065,2066,2067,642],{},"Where the credentials those clients need come from: ",[653,2068,2070],{"href":2069},"\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002F","Secrets and .env Files Per Environment",[600,2072,2073,2074,642],{},"The per-request counterpart to an app-lifetime pool: ",[653,2075,1959],{"href":1958},[2077,2078,2079],"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 .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}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 .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":885,"searchDepth":898,"depth":898,"links":2081},[2082,2083,2084,2085,2086,2087,2088],{"id":661,"depth":898,"text":662},{"id":794,"depth":898,"text":795},{"id":868,"depth":898,"text":869},{"id":1776,"depth":898,"text":1777},{"id":1882,"depth":898,"text":1883},{"id":1963,"depth":898,"text":1964},{"id":2040,"depth":898,"text":2041},"2026-07-20","Replace deprecated @app.on_event with the lifespan async context manager: ordering guarantees, shared clients on app.state, and clean teardown in reverse order.","md",[2093,2095,2097,2099,2101],{"q":1969,"a":2094},"Yes. FastAPI deprecated the on_event decorators in favour of the lifespan argument, and Starlette has done the same. They still work, but new code should use lifespan and existing code should migrate — the two mechanisms cannot be mixed on one application.",{"q":1984,"a":2096},"Everything before the yield completes before the server accepts a single request, and everything after the yield runs only once the server has stopped accepting them. Within the function, statements run in the order you wrote them, so teardown in reverse acquisition order is just the natural shape of a try\u002Ffinally block.",{"q":2001,"a":2098},"Use app.state for things a background task, an exception handler or a startup check might also need, since it is reachable from request.app.state anywhere. Use the yielded mapping for values you only ever read inside a request, since it lands on request.state and is typed a little more naturally.",{"q":2017,"a":2100},"Only when you use TestClient as a context manager. A bare TestClient(app) skips the lifespan entirely, which is why tests suddenly fail with a missing attribute on app.state — wrap it in a with block and startup runs.",{"q":2036,"a":2102},"The lifespan fails, the ASGI server logs the error and exits without binding the port, so the application never serves traffic in a half-initialised state. That is the desired behaviour: a database that is unreachable at boot should stop the deploy, not produce a pod that 500s on every request.",null,{"slug":2105,"breadcrumb":2106},"lifespan-events-vs-startup-shutdown",[2107,2109,2112,2113],{"label":2108,"path":624},"Home",{"label":2110,"path":2111},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":656,"path":655},{"label":2114,"path":2115},"Lifespan Events vs Startup and Shutdown","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002F",{"title":407,"description":2090},"article","scl0qMSMcCu38HjEovXnV8snBUCigT7CUeOB2_tikO8",[2103,2103],1784588202739]