[{"data":1,"prerenderedAt":2303},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002F":580,"surround-\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002F":2302},[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":563,"body":582,"dateModified":2272,"datePublished":2272,"description":2273,"extension":2274,"faq":2275,"howto":2286,"meta":2287,"navigation":850,"path":564,"seo":2299,"stem":565,"type":2300,"__hash__":2301},"content\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex.md",{"type":583,"value":584,"toc":2262},"minimark",[585,589,596,629,637,752,757,771,774,778,781,804,810,1831,1849,1853,1860,1867,1870,1874,1887,1903,1924,1940,1949,1973,1979,1983,1990,2110,2120,2124,2127,2134,2144,2150,2154,2169,2187,2202,2211,2215,2258],[586,587,563],"h1",{"id":588},"how-a-request-flows-through-fastapi",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,603,606,614,622],"ul",{},[600,601,602],"li",{},"Registration order and execution order are opposites: the middleware you add last runs first.",[600,604,605],{},"Dependencies resolve depth-first after all middleware entry code and before the path operation.",[600,607,608,609,613],{},"Your handler's return value is serialized ",[610,611,612],"em",{},"inside"," the router — middleware only ever sees finished bytes.",[600,615,616,617,621],{},"Background tasks run after the body is sent; ",[618,619,620],"code",{},"yield"," teardown runs after the background tasks.",[600,623,624,625,628],{},"With pure ASGI middleware the whole sequence is strictly nested and reproducible; with ",[618,626,627],{},"BaseHTTPMiddleware"," it is not.",[590,630,631,632,636],{},"Nearly every FastAPI discussion about ordering is conducted in prose. Someone asserts that middleware runs before dependencies, someone else replies that it depends, and the thread ends without evidence. This page answers the question by instrumenting an application so that it reports its own execution order, then showing what it actually reported. The conceptual model behind the trace lives in ",[633,634,557],"a",{"href":635},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002F","; this page is the measurement.",[638,639,645,646,645,650,645,654,645,661,645,665,645,674,645,680,645,683,645,687,645,690,645,694,645,697,645,701,645,705,645,710,645,713,645,717,645,719,645,722,645,724,645,727,645,729,645,732,645,734,645,737,645,742,645,748],"svg",{"viewBox":640,"role":641,"ariaLabel":642,"xmlns":643,"style":644},"0 0 720 330","img","Two-column diagram showing the descent order and the ascent order of a single FastAPI request","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[647,648,649],"title",{},"Descent and ascent order of one traced FastAPI request",[651,652,653],"desc",{},"The left column lists steps one to seven going inward: outer middleware, middle middleware, inner middleware, dependency resolution, then the path operation. The right column lists steps eight to eighteen going back outward: response start, body chunks, background task, dependency teardown, and finally the middleware exit code.",[655,656,660],"text",{"x":657,"y":658,"style":659},"180","26","text-anchor:middle;fill:#00796B;font:700 14px sans-serif","Descent (inward)",[655,662,664],{"x":663,"y":658,"style":659},"540","Ascent (outward)",[666,667],"rect",{"x":668,"y":669,"width":670,"height":671,"rx":672,"style":673},"30","42","300","34","7","fill:none;stroke:currentColor;stroke-width:1.4",[655,675,679],{"x":676,"y":677,"style":678},"44","64","text-anchor:start;fill:currentColor;font:400 12px sans-serif","01 middleware outer (added last)",[666,681],{"x":668,"y":682,"width":670,"height":671,"rx":672,"style":673},"84",[655,684,686],{"x":676,"y":685,"style":678},"106","02 middleware middle",[666,688],{"x":668,"y":689,"width":670,"height":671,"rx":672,"style":673},"126",[655,691,693],{"x":676,"y":692,"style":678},"148","03 middleware inner (added first)",[666,695],{"x":668,"y":696,"width":670,"height":671,"rx":672,"style":673},"168",[655,698,700],{"x":676,"y":699,"style":678},"190","04-05 dependencies, depth first",[666,702],{"x":668,"y":703,"width":670,"height":671,"rx":672,"style":704},"210","fill:#00796B;stroke:#00796B;stroke-width:1.4",[655,706,709],{"x":676,"y":707,"style":708},"232","text-anchor:start;fill:#ffffff;font:600 12px sans-serif","06-07 path operation returns",[666,711],{"x":712,"y":669,"width":670,"height":671,"rx":672,"style":673},"390",[655,714,716],{"x":715,"y":677,"style":678},"404","16-18 middleware exit code",[666,718],{"x":712,"y":682,"width":670,"height":671,"rx":672,"style":673},[655,720,721],{"x":715,"y":685,"style":678},"15 yield dependency teardown",[666,723],{"x":712,"y":689,"width":670,"height":671,"rx":672,"style":673},[655,725,726],{"x":715,"y":692,"style":678},"14 background task executes",[666,728],{"x":712,"y":696,"width":670,"height":671,"rx":672,"style":673},[655,730,731],{"x":715,"y":699,"style":678},"11-13 final body chunk outward",[666,733],{"x":712,"y":703,"width":670,"height":671,"rx":672,"style":704},[655,735,736],{"x":715,"y":707,"style":708},"08-10 response.start outward",[738,739],"path",{"d":740,"style":741},"M345 244 L375 244","fill:none;stroke:#00796B;stroke-width:2",[655,743,747],{"x":744,"y":745,"style":746},"360","290","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","Step numbers are the real positions from the captured trace below.",[655,749,751],{"x":744,"y":750,"style":746},"312","The ascent column reads bottom-to-top, mirroring the descent.",[753,754,756],"h2",{"id":755},"the-scenario","The scenario",[590,758,759,760,763,764,766,767,770],{},"You have three middleware classes and you cannot remember which one sees the request first. Or you added a ",[618,761,762],{},"BackgroundTasks"," job that touches the database session from a ",[618,765,620],{}," dependency, and it works locally but intermittently fails against a real connection pool. Or a colleague insists that ",[618,768,769],{},"response_model"," validation happens \"after middleware\", and you suspect it does not.",[590,772,773],{},"All three are the same question wearing different clothes: at what point in the sequence does each thing happen? A trace answers all three at once.",[753,775,777],{"id":776},"the-instrumented-application","The instrumented application",[590,779,780],{},"The design constraint is that the trace must not disturb what it measures. Two decisions matter.",[590,782,783,784,787,788,791,792,794,795,798,799,803],{},"First, every middleware here is ",[593,785,786],{},"pure ASGI"," — a class with ",[618,789,790],{},"__call__(self, scope, receive, send)",". That is deliberate. ",[618,793,627],{}," runs the inner application inside an AnyIO task group and streams the response back through a memory object stream, which means its post-",[618,796,797],{},"call_next"," code can resume before the body has finished travelling. Pure ASGI middleware has no such indirection, so \"after the next layer returned\" means exactly that. If you want reproducible ordering in your own application, this is the reason to prefer it — a point argued from the performance angle in ",[633,800,802],{"href":801},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","Middleware Implementation",".",[590,805,806,807,803],{},"Second, the trace is read back over a second request. There is no way to report the tail of a request inside its own response body, because the tail happens after the body is sent. So the app appends to a module-level list and exposes it at ",[618,808,809],{},"\u002Fsteps",[811,812,817],"pre",{"className":813,"code":814,"language":815,"meta":816,"style":816},"language-python shiki shiki-themes github-light-high-contrast","\"\"\"A single request traced with pure-ASGI middleware only, so the ordering is unambiguous.\"\"\"\nfrom typing import Annotated\n\nfrom fastapi import BackgroundTasks, Depends, FastAPI\n\nSTEPS: list[str] = []\n\n\ndef step(event: str) -> None:\n    STEPS.append(f\"{len(STEPS) + 1:02d}. {event}\")\n\n\nclass Layer:\n    \"\"\"Pure ASGI middleware. No task groups, no buffering — exit really means exit.\"\"\"\n\n    def __init__(self, app, name: str) -> None:\n        self.app = app\n        self.name = name\n\n    async def __call__(self, scope, receive, send) -> None:\n        if scope[\"type\"] != \"http\" or scope[\"path\"] == \"\u002Fsteps\":\n            await self.app(scope, receive, send)\n            return\n        if self.name == \"outer\":\n            STEPS.clear()\n        step(f\"middleware {self.name}: BEFORE next layer\")\n\n        async def send_wrapper(message) -> None:\n            if message[\"type\"] == \"http.response.start\":\n                step(f\"middleware {self.name}: saw response.start ({message['status']})\")\n            elif message[\"type\"] == \"http.response.body\" and not message.get(\"more_body\"):\n                step(f\"middleware {self.name}: saw final body chunk\")\n            await send(message)\n\n        await self.app(scope, receive, send_wrapper)\n        step(f\"middleware {self.name}: AFTER next layer returned\")\n\n\napp = FastAPI()\n\n\nasync def auth() -> str:\n    step(\"dependency auth(): resolved\")\n    return \"user-42\"\n\n\nasync def db(user: Annotated[str, Depends(auth)]):\n    step(\"dependency db(): opened (depends on auth)\")\n    try:\n        yield f\"session-for-{user}\"\n    finally:\n        step(\"dependency db(): closed\")\n\n\ndef after_response() -> None:\n    step(\"background task: executed\")\n\n\n@app.get(\"\u002Forder\u002F{order_id}\")\nasync def read_order(\n    order_id: int,\n    session: Annotated[str, Depends(db)],\n    tasks: BackgroundTasks,\n) -> dict[str, str]:\n    step(f\"path operation: running with order_id={order_id}\")\n    tasks.add_task(after_response)\n    step(\"path operation: returning dict (not yet serialized)\")\n    return {\"order_id\": str(order_id), \"session\": session}\n\n\n@app.get(\"\u002Fsteps\")\nasync def steps() -> dict[str, list[str]]:\n    return {\"timeline\": list(STEPS)}\n\n\napp.add_middleware(Layer, name=\"inner\")\napp.add_middleware(Layer, name=\"middle\")\napp.add_middleware(Layer, name=\"outer\")\n","python","",[618,818,819,828,845,852,865,870,892,897,902,926,980,985,990,1002,1008,1013,1033,1047,1060,1065,1084,1122,1134,1140,1156,1165,1191,1196,1214,1234,1273,1305,1327,1335,1340,1351,1373,1378,1383,1394,1399,1404,1422,1433,1442,1447,1452,1470,1480,1488,1510,1518,1528,1533,1538,1552,1562,1567,1572,1590,1603,1615,1626,1632,1648,1669,1675,1685,1710,1715,1720,1732,1755,1777,1782,1787,1803,1817],{"__ignoreMap":816},[820,821,824],"span",{"class":822,"line":823},"line",1,[820,825,827],{"class":826},"sYEJz","\"\"\"A single request traced with pure-ASGI middleware only, so the ordering is unambiguous.\"\"\"\n",[820,829,831,835,839,842],{"class":822,"line":830},2,[820,832,834],{"class":833},"sTJeM","from",[820,836,838],{"class":837},"sigWx"," typing ",[820,840,841],{"class":833},"import",[820,843,844],{"class":837}," Annotated\n",[820,846,848],{"class":822,"line":847},3,[820,849,851],{"emptyLinePlaceholder":850},true,"\n",[820,853,855,857,860,862],{"class":822,"line":854},4,[820,856,834],{"class":833},[820,858,859],{"class":837}," fastapi ",[820,861,841],{"class":833},[820,863,864],{"class":837}," BackgroundTasks, Depends, FastAPI\n",[820,866,868],{"class":822,"line":867},5,[820,869,851],{"emptyLinePlaceholder":850},[820,871,873,877,880,883,886,889],{"class":822,"line":872},6,[820,874,876],{"class":875},"sacAq","STEPS",[820,878,879],{"class":837},": list[",[820,881,882],{"class":875},"str",[820,884,885],{"class":837},"] ",[820,887,888],{"class":833},"=",[820,890,891],{"class":837}," []\n",[820,893,895],{"class":822,"line":894},7,[820,896,851],{"emptyLinePlaceholder":850},[820,898,900],{"class":822,"line":899},8,[820,901,851],{"emptyLinePlaceholder":850},[820,903,905,908,912,915,917,920,923],{"class":822,"line":904},9,[820,906,907],{"class":833},"def",[820,909,911],{"class":910},"s3dhs"," step",[820,913,914],{"class":837},"(event: ",[820,916,882],{"class":875},[820,918,919],{"class":837},") -> ",[820,921,922],{"class":875},"None",[820,924,925],{"class":837},":\n",[820,927,929,932,935,938,941,944,947,950,952,955,958,961,964,967,969,972,975,977],{"class":822,"line":928},10,[820,930,931],{"class":875},"    STEPS",[820,933,934],{"class":837},".append(",[820,936,937],{"class":833},"f",[820,939,940],{"class":826},"\"",[820,942,943],{"class":833},"{",[820,945,946],{"class":875},"len",[820,948,949],{"class":837},"(",[820,951,876],{"class":875},[820,953,954],{"class":837},") ",[820,956,957],{"class":833},"+",[820,959,960],{"class":875}," 1",[820,962,963],{"class":833},":02d}",[820,965,966],{"class":826},". ",[820,968,943],{"class":833},[820,970,971],{"class":837},"event",[820,973,974],{"class":833},"}",[820,976,940],{"class":826},[820,978,979],{"class":837},")\n",[820,981,983],{"class":822,"line":982},11,[820,984,851],{"emptyLinePlaceholder":850},[820,986,988],{"class":822,"line":987},12,[820,989,851],{"emptyLinePlaceholder":850},[820,991,993,996,1000],{"class":822,"line":992},13,[820,994,995],{"class":833},"class",[820,997,999],{"class":998},"sV4o_"," Layer",[820,1001,925],{"class":837},[820,1003,1005],{"class":822,"line":1004},14,[820,1006,1007],{"class":826},"    \"\"\"Pure ASGI middleware. No task groups, no buffering — exit really means exit.\"\"\"\n",[820,1009,1011],{"class":822,"line":1010},15,[820,1012,851],{"emptyLinePlaceholder":850},[820,1014,1016,1019,1022,1025,1027,1029,1031],{"class":822,"line":1015},16,[820,1017,1018],{"class":833},"    def",[820,1020,1021],{"class":875}," __init__",[820,1023,1024],{"class":837},"(self, app, name: ",[820,1026,882],{"class":875},[820,1028,919],{"class":837},[820,1030,922],{"class":875},[820,1032,925],{"class":837},[820,1034,1036,1039,1042,1044],{"class":822,"line":1035},17,[820,1037,1038],{"class":875},"        self",[820,1040,1041],{"class":837},".app ",[820,1043,888],{"class":833},[820,1045,1046],{"class":837}," app\n",[820,1048,1050,1052,1055,1057],{"class":822,"line":1049},18,[820,1051,1038],{"class":875},[820,1053,1054],{"class":837},".name ",[820,1056,888],{"class":833},[820,1058,1059],{"class":837}," name\n",[820,1061,1063],{"class":822,"line":1062},19,[820,1064,851],{"emptyLinePlaceholder":850},[820,1066,1068,1071,1074,1077,1080,1082],{"class":822,"line":1067},20,[820,1069,1070],{"class":833},"    async",[820,1072,1073],{"class":833}," def",[820,1075,1076],{"class":875}," __call__",[820,1078,1079],{"class":837},"(self, scope, receive, send) -> ",[820,1081,922],{"class":875},[820,1083,925],{"class":837},[820,1085,1087,1090,1093,1096,1098,1101,1104,1107,1109,1112,1114,1117,1120],{"class":822,"line":1086},21,[820,1088,1089],{"class":833},"        if",[820,1091,1092],{"class":837}," scope[",[820,1094,1095],{"class":826},"\"type\"",[820,1097,885],{"class":837},[820,1099,1100],{"class":833},"!=",[820,1102,1103],{"class":826}," \"http\"",[820,1105,1106],{"class":833}," or",[820,1108,1092],{"class":837},[820,1110,1111],{"class":826},"\"path\"",[820,1113,885],{"class":837},[820,1115,1116],{"class":833},"==",[820,1118,1119],{"class":826}," \"\u002Fsteps\"",[820,1121,925],{"class":837},[820,1123,1125,1128,1131],{"class":822,"line":1124},22,[820,1126,1127],{"class":833},"            await",[820,1129,1130],{"class":875}," self",[820,1132,1133],{"class":837},".app(scope, receive, send)\n",[820,1135,1137],{"class":822,"line":1136},23,[820,1138,1139],{"class":833},"            return\n",[820,1141,1143,1145,1147,1149,1151,1154],{"class":822,"line":1142},24,[820,1144,1089],{"class":833},[820,1146,1130],{"class":875},[820,1148,1054],{"class":837},[820,1150,1116],{"class":833},[820,1152,1153],{"class":826}," \"outer\"",[820,1155,925],{"class":837},[820,1157,1159,1162],{"class":822,"line":1158},25,[820,1160,1161],{"class":875},"            STEPS",[820,1163,1164],{"class":837},".clear()\n",[820,1166,1168,1171,1173,1176,1178,1181,1184,1186,1189],{"class":822,"line":1167},26,[820,1169,1170],{"class":837},"        step(",[820,1172,937],{"class":833},[820,1174,1175],{"class":826},"\"middleware ",[820,1177,943],{"class":833},[820,1179,1180],{"class":875},"self",[820,1182,1183],{"class":837},".name",[820,1185,974],{"class":833},[820,1187,1188],{"class":826},": BEFORE next layer\"",[820,1190,979],{"class":837},[820,1192,1194],{"class":822,"line":1193},27,[820,1195,851],{"emptyLinePlaceholder":850},[820,1197,1199,1202,1204,1207,1210,1212],{"class":822,"line":1198},28,[820,1200,1201],{"class":833},"        async",[820,1203,1073],{"class":833},[820,1205,1206],{"class":910}," send_wrapper",[820,1208,1209],{"class":837},"(message) -> ",[820,1211,922],{"class":875},[820,1213,925],{"class":837},[820,1215,1217,1220,1223,1225,1227,1229,1232],{"class":822,"line":1216},29,[820,1218,1219],{"class":833},"            if",[820,1221,1222],{"class":837}," message[",[820,1224,1095],{"class":826},[820,1226,885],{"class":837},[820,1228,1116],{"class":833},[820,1230,1231],{"class":826}," \"http.response.start\"",[820,1233,925],{"class":837},[820,1235,1237,1240,1242,1244,1246,1248,1250,1252,1255,1257,1260,1263,1266,1268,1271],{"class":822,"line":1236},30,[820,1238,1239],{"class":837},"                step(",[820,1241,937],{"class":833},[820,1243,1175],{"class":826},[820,1245,943],{"class":833},[820,1247,1180],{"class":875},[820,1249,1183],{"class":837},[820,1251,974],{"class":833},[820,1253,1254],{"class":826},": saw response.start (",[820,1256,943],{"class":833},[820,1258,1259],{"class":837},"message[",[820,1261,1262],{"class":826},"'status'",[820,1264,1265],{"class":837},"]",[820,1267,974],{"class":833},[820,1269,1270],{"class":826},")\"",[820,1272,979],{"class":837},[820,1274,1276,1279,1281,1283,1285,1287,1290,1293,1296,1299,1302],{"class":822,"line":1275},31,[820,1277,1278],{"class":833},"            elif",[820,1280,1222],{"class":837},[820,1282,1095],{"class":826},[820,1284,885],{"class":837},[820,1286,1116],{"class":833},[820,1288,1289],{"class":826}," \"http.response.body\"",[820,1291,1292],{"class":833}," and",[820,1294,1295],{"class":833}," not",[820,1297,1298],{"class":837}," message.get(",[820,1300,1301],{"class":826},"\"more_body\"",[820,1303,1304],{"class":837},"):\n",[820,1306,1308,1310,1312,1314,1316,1318,1320,1322,1325],{"class":822,"line":1307},32,[820,1309,1239],{"class":837},[820,1311,937],{"class":833},[820,1313,1175],{"class":826},[820,1315,943],{"class":833},[820,1317,1180],{"class":875},[820,1319,1183],{"class":837},[820,1321,974],{"class":833},[820,1323,1324],{"class":826},": saw final body chunk\"",[820,1326,979],{"class":837},[820,1328,1330,1332],{"class":822,"line":1329},33,[820,1331,1127],{"class":833},[820,1333,1334],{"class":837}," send(message)\n",[820,1336,1338],{"class":822,"line":1337},34,[820,1339,851],{"emptyLinePlaceholder":850},[820,1341,1343,1346,1348],{"class":822,"line":1342},35,[820,1344,1345],{"class":833},"        await",[820,1347,1130],{"class":875},[820,1349,1350],{"class":837},".app(scope, receive, send_wrapper)\n",[820,1352,1354,1356,1358,1360,1362,1364,1366,1368,1371],{"class":822,"line":1353},36,[820,1355,1170],{"class":837},[820,1357,937],{"class":833},[820,1359,1175],{"class":826},[820,1361,943],{"class":833},[820,1363,1180],{"class":875},[820,1365,1183],{"class":837},[820,1367,974],{"class":833},[820,1369,1370],{"class":826},": AFTER next layer returned\"",[820,1372,979],{"class":837},[820,1374,1376],{"class":822,"line":1375},37,[820,1377,851],{"emptyLinePlaceholder":850},[820,1379,1381],{"class":822,"line":1380},38,[820,1382,851],{"emptyLinePlaceholder":850},[820,1384,1386,1389,1391],{"class":822,"line":1385},39,[820,1387,1388],{"class":837},"app ",[820,1390,888],{"class":833},[820,1392,1393],{"class":837}," FastAPI()\n",[820,1395,1397],{"class":822,"line":1396},40,[820,1398,851],{"emptyLinePlaceholder":850},[820,1400,1402],{"class":822,"line":1401},41,[820,1403,851],{"emptyLinePlaceholder":850},[820,1405,1407,1410,1412,1415,1418,1420],{"class":822,"line":1406},42,[820,1408,1409],{"class":833},"async",[820,1411,1073],{"class":833},[820,1413,1414],{"class":910}," auth",[820,1416,1417],{"class":837},"() -> ",[820,1419,882],{"class":875},[820,1421,925],{"class":837},[820,1423,1425,1428,1431],{"class":822,"line":1424},43,[820,1426,1427],{"class":837},"    step(",[820,1429,1430],{"class":826},"\"dependency auth(): resolved\"",[820,1432,979],{"class":837},[820,1434,1436,1439],{"class":822,"line":1435},44,[820,1437,1438],{"class":833},"    return",[820,1440,1441],{"class":826}," \"user-42\"\n",[820,1443,1445],{"class":822,"line":1444},45,[820,1446,851],{"emptyLinePlaceholder":850},[820,1448,1450],{"class":822,"line":1449},46,[820,1451,851],{"emptyLinePlaceholder":850},[820,1453,1455,1457,1459,1462,1465,1467],{"class":822,"line":1454},47,[820,1456,1409],{"class":833},[820,1458,1073],{"class":833},[820,1460,1461],{"class":910}," db",[820,1463,1464],{"class":837},"(user: Annotated[",[820,1466,882],{"class":875},[820,1468,1469],{"class":837},", Depends(auth)]):\n",[820,1471,1473,1475,1478],{"class":822,"line":1472},48,[820,1474,1427],{"class":837},[820,1476,1477],{"class":826},"\"dependency db(): opened (depends on auth)\"",[820,1479,979],{"class":837},[820,1481,1483,1486],{"class":822,"line":1482},49,[820,1484,1485],{"class":833},"    try",[820,1487,925],{"class":837},[820,1489,1491,1494,1497,1500,1502,1505,1507],{"class":822,"line":1490},50,[820,1492,1493],{"class":833},"        yield",[820,1495,1496],{"class":833}," f",[820,1498,1499],{"class":826},"\"session-for-",[820,1501,943],{"class":833},[820,1503,1504],{"class":837},"user",[820,1506,974],{"class":833},[820,1508,1509],{"class":826},"\"\n",[820,1511,1513,1516],{"class":822,"line":1512},51,[820,1514,1515],{"class":833},"    finally",[820,1517,925],{"class":837},[820,1519,1521,1523,1526],{"class":822,"line":1520},52,[820,1522,1170],{"class":837},[820,1524,1525],{"class":826},"\"dependency db(): closed\"",[820,1527,979],{"class":837},[820,1529,1531],{"class":822,"line":1530},53,[820,1532,851],{"emptyLinePlaceholder":850},[820,1534,1536],{"class":822,"line":1535},54,[820,1537,851],{"emptyLinePlaceholder":850},[820,1539,1541,1543,1546,1548,1550],{"class":822,"line":1540},55,[820,1542,907],{"class":833},[820,1544,1545],{"class":910}," after_response",[820,1547,1417],{"class":837},[820,1549,922],{"class":875},[820,1551,925],{"class":837},[820,1553,1555,1557,1560],{"class":822,"line":1554},56,[820,1556,1427],{"class":837},[820,1558,1559],{"class":826},"\"background task: executed\"",[820,1561,979],{"class":837},[820,1563,1565],{"class":822,"line":1564},57,[820,1566,851],{"emptyLinePlaceholder":850},[820,1568,1570],{"class":822,"line":1569},58,[820,1571,851],{"emptyLinePlaceholder":850},[820,1573,1575,1578,1580,1583,1586,1588],{"class":822,"line":1574},59,[820,1576,1577],{"class":910},"@app.get",[820,1579,949],{"class":837},[820,1581,1582],{"class":826},"\"\u002Forder\u002F",[820,1584,1585],{"class":833},"{order_id}",[820,1587,940],{"class":826},[820,1589,979],{"class":837},[820,1591,1593,1595,1597,1600],{"class":822,"line":1592},60,[820,1594,1409],{"class":833},[820,1596,1073],{"class":833},[820,1598,1599],{"class":910}," read_order",[820,1601,1602],{"class":837},"(\n",[820,1604,1606,1609,1612],{"class":822,"line":1605},61,[820,1607,1608],{"class":837},"    order_id: ",[820,1610,1611],{"class":875},"int",[820,1613,1614],{"class":837},",\n",[820,1616,1618,1621,1623],{"class":822,"line":1617},62,[820,1619,1620],{"class":837},"    session: Annotated[",[820,1622,882],{"class":875},[820,1624,1625],{"class":837},", Depends(db)],\n",[820,1627,1629],{"class":822,"line":1628},63,[820,1630,1631],{"class":837},"    tasks: BackgroundTasks,\n",[820,1633,1635,1638,1640,1643,1645],{"class":822,"line":1634},64,[820,1636,1637],{"class":837},") -> dict[",[820,1639,882],{"class":875},[820,1641,1642],{"class":837},", ",[820,1644,882],{"class":875},[820,1646,1647],{"class":837},"]:\n",[820,1649,1651,1653,1655,1658,1660,1663,1665,1667],{"class":822,"line":1650},65,[820,1652,1427],{"class":837},[820,1654,937],{"class":833},[820,1656,1657],{"class":826},"\"path operation: running with order_id=",[820,1659,943],{"class":833},[820,1661,1662],{"class":837},"order_id",[820,1664,974],{"class":833},[820,1666,940],{"class":826},[820,1668,979],{"class":837},[820,1670,1672],{"class":822,"line":1671},66,[820,1673,1674],{"class":837},"    tasks.add_task(after_response)\n",[820,1676,1678,1680,1683],{"class":822,"line":1677},67,[820,1679,1427],{"class":837},[820,1681,1682],{"class":826},"\"path operation: returning dict (not yet serialized)\"",[820,1684,979],{"class":837},[820,1686,1688,1690,1693,1696,1699,1701,1704,1707],{"class":822,"line":1687},68,[820,1689,1438],{"class":833},[820,1691,1692],{"class":837}," {",[820,1694,1695],{"class":826},"\"order_id\"",[820,1697,1698],{"class":837},": ",[820,1700,882],{"class":875},[820,1702,1703],{"class":837},"(order_id), ",[820,1705,1706],{"class":826},"\"session\"",[820,1708,1709],{"class":837},": session}\n",[820,1711,1713],{"class":822,"line":1712},69,[820,1714,851],{"emptyLinePlaceholder":850},[820,1716,1718],{"class":822,"line":1717},70,[820,1719,851],{"emptyLinePlaceholder":850},[820,1721,1723,1725,1727,1730],{"class":822,"line":1722},71,[820,1724,1577],{"class":910},[820,1726,949],{"class":837},[820,1728,1729],{"class":826},"\"\u002Fsteps\"",[820,1731,979],{"class":837},[820,1733,1735,1737,1739,1742,1745,1747,1750,1752],{"class":822,"line":1734},72,[820,1736,1409],{"class":833},[820,1738,1073],{"class":833},[820,1740,1741],{"class":910}," steps",[820,1743,1744],{"class":837},"() -> dict[",[820,1746,882],{"class":875},[820,1748,1749],{"class":837},", list[",[820,1751,882],{"class":875},[820,1753,1754],{"class":837},"]]:\n",[820,1756,1758,1760,1762,1765,1767,1770,1772,1774],{"class":822,"line":1757},73,[820,1759,1438],{"class":833},[820,1761,1692],{"class":837},[820,1763,1764],{"class":826},"\"timeline\"",[820,1766,1698],{"class":837},[820,1768,1769],{"class":875},"list",[820,1771,949],{"class":837},[820,1773,876],{"class":875},[820,1775,1776],{"class":837},")}\n",[820,1778,1780],{"class":822,"line":1779},74,[820,1781,851],{"emptyLinePlaceholder":850},[820,1783,1785],{"class":822,"line":1784},75,[820,1786,851],{"emptyLinePlaceholder":850},[820,1788,1790,1793,1796,1798,1801],{"class":822,"line":1789},76,[820,1791,1792],{"class":837},"app.add_middleware(Layer, ",[820,1794,1795],{"class":998},"name",[820,1797,888],{"class":833},[820,1799,1800],{"class":826},"\"inner\"",[820,1802,979],{"class":837},[820,1804,1806,1808,1810,1812,1815],{"class":822,"line":1805},77,[820,1807,1792],{"class":837},[820,1809,1795],{"class":998},[820,1811,888],{"class":833},[820,1813,1814],{"class":826},"\"middle\"",[820,1816,979],{"class":837},[820,1818,1820,1822,1824,1826,1829],{"class":822,"line":1819},78,[820,1821,1792],{"class":837},[820,1823,1795],{"class":998},[820,1825,888],{"class":833},[820,1827,1828],{"class":826},"\"outer\"",[820,1830,979],{"class":837},[590,1832,1833,1834,1837,1838,1837,1841,1844,1845,1848],{},"Note the registration block at the bottom: ",[618,1835,1836],{},"inner",", then ",[618,1839,1840],{},"middle",[618,1842,1843],{},"outer",". The names describe where each one ",[610,1846,1847],{},"ends up",", which is the reverse of the order they were written.",[753,1850,1852],{"id":1851},"the-captured-trace","The captured trace",[590,1854,1855,1856,1859],{},"This is the real output of running the app above in-process, from ",[618,1857,1858],{},"_verify\u002Fexamples\u002Farch-request-flow-timeline.py",":",[811,1861,1865],{"className":1862,"code":1864,"language":655,"meta":816},[1863],"language-text","$ GET \u002Forder\u002F7\n200 OK\n{\n  \"order_id\": \"7\",\n  \"session\": \"session-for-user-42\"\n}\n\n$ GET \u002Fsteps\n200 OK\n{\n  \"timeline\": [\n    \"01. middleware outer: BEFORE next layer\",\n    \"02. middleware middle: BEFORE next layer\",\n    \"03. middleware inner: BEFORE next layer\",\n    \"04. dependency auth(): resolved\",\n    \"05. dependency db(): opened (depends on auth)\",\n    \"06. path operation: running with order_id=7\",\n    \"07. path operation: returning dict (not yet serialized)\",\n    \"08. middleware inner: saw response.start (200)\",\n    \"09. middleware middle: saw response.start (200)\",\n    \"10. middleware outer: saw response.start (200)\",\n    \"11. middleware inner: saw final body chunk\",\n    \"12. middleware middle: saw final body chunk\",\n    \"13. middleware outer: saw final body chunk\",\n    \"14. background task: executed\",\n    \"15. dependency db(): closed\",\n    \"16. middleware inner: AFTER next layer returned\",\n    \"17. middleware middle: AFTER next layer returned\",\n    \"18. middleware outer: AFTER next layer returned\"\n  ]\n}\n",[618,1866,1864],{"__ignoreMap":816},[590,1868,1869],{},"Eighteen steps, and every disputed claim is settled by one of them.",[753,1871,1873],{"id":1872},"reading-the-trace","Reading the trace",[590,1875,1876,1879,1880,1882,1883,1886],{},[593,1877,1878],{},"Steps 01–03 invert registration order."," ",[618,1881,1843],{}," was registered last and ran first. There is no configuration involved: ",[618,1884,1885],{},"add_middleware"," prepends, and Starlette builds the ASGI chain by wrapping from the router outward, so each registration wraps everything before it. The corollary worth internalising is that adding a middleware can never accidentally exclude an existing one from observation — a new registration always sits outside. If you have ever wondered why your timing middleware reports a suspiciously low number, check whether something was registered after it.",[590,1888,1889,1879,1892,1895,1896,1899,1900,803],{},[593,1890,1891],{},"Steps 04–05 show dependency resolution is depth first.",[618,1893,1894],{},"auth()"," is a sub-dependency of ",[618,1897,1898],{},"db()",", and it resolved first. FastAPI computed this tree at import time when the route was declared, so the per-request work is solving a frozen graph, not re-introspecting signatures. Nothing in the middleware layer is involved. If you need per-route logic that reads parsed inputs, this is the layer you want, and the choice is laid out in ",[633,1901,521],{"href":1902},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F",[590,1904,1905,1908,1909,1912,1913,1916,1917,1920,1921,803],{},[593,1906,1907],{},"Step 07 is the sharpest result on the page."," The path operation logs that it is returning a plain ",[618,1910,1911],{},"dict",", and the very next thing any layer observes is step 08: ",[618,1914,1915],{},"response.start"," with a status code. Between those two steps FastAPI validated the return value, applied the response field, ran ",[618,1918,1919],{},"jsonable_encoder"," and produced bytes — all inside the router, invisible to every middleware. This is the empirical answer to \"does middleware see my model or my JSON?\" It sees JSON. It has always seen JSON. Anything you want to do with the typed object must happen in the handler or a dependency, never in middleware. What that serialization step actually does to your object is the subject of ",[633,1922,569],{"href":1923},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002F",[590,1925,1926,1929,1930,1933,1934,1936,1937,803],{},[593,1927,1928],{},"Steps 08–13 unwind innermost first",", and they unwind twice: once for the ",[618,1931,1932],{},"http.response.start"," message carrying status and headers, once for the body. This split is why header mutation must happen before ",[618,1935,1915],{}," is forwarded — by step 11 the headers are gone. A middleware that tries to append a header after seeing the body chunk is writing to a message nobody will read. This is the single most common bug in hand-rolled tracing middleware; the correct pattern is in ",[633,1938,509],{"href":1939},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F",[590,1941,1942,1945,1946,1948],{},[593,1943,1944],{},"Step 14 confirms background tasks run after the client has its bytes."," That is the entire point of ",[618,1947,762],{},", and the trace shows the guarantee is real: steps 11–13 delivered the final body chunk before step 14 fired. The client's latency does not include the task.",[590,1950,1951,1879,1954,1956,1957,1960,1961,1963,1964,1968,1969,803],{},[593,1952,1953],{},"Step 15 is the one that ends arguments.",[618,1955,620],{}," dependency teardown ran ",[610,1958,1959],{},"after"," the background task, not before it. Read the practical consequence carefully: a background task that uses a database session obtained from a ",[618,1962,620],{}," dependency happens to work in FastAPI 0.139.2, because the session is still open at step 14. But this ordering is an implementation detail of when the exit stack is unwound, not a documented contract, and it has moved between FastAPI releases. Treat a background task as a separate unit of work that acquires its own resources. The failure shapes are catalogued in ",[633,1965,1967],{"href":1966},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002F","When BackgroundTasks Silently Fails",", and the general rules for exit-stack unwinding are in ",[633,1970,1972],{"href":1971},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F","Yield Dependencies and Cleanup Order",[590,1974,1975,1978],{},[593,1976,1977],{},"Steps 16–18 close the nesting."," Every middleware's post-call code runs, innermost first. Because these are pure ASGI middleware, \"after the next layer returned\" is unambiguous and the sequence is perfectly nested against steps 01–03.",[753,1980,1982],{"id":1981},"verification-reproduce-this-in-your-own-app","Verification: reproduce this in your own app",[590,1984,1985,1986,1989],{},"You do not need a special harness. Drop a module-level list into a test module, wrap your real middleware in the ",[618,1987,1988],{},"Layer"," pattern above, and assert on positions rather than on presence:",[811,1991,1993],{"className":813,"code":1992,"language":815,"meta":816,"style":816},"def test_middleware_registration_order(client):\n    resp = client.get(\"\u002Forder\u002F7\")\n    assert resp.status_code == 200\n    timeline = client.get(\"\u002Fsteps\").json()[\"timeline\"]\n    entry = [s for s in timeline if \"BEFORE next layer\" in s]\n    assert entry[0].endswith(\"outer: BEFORE next layer\")\n    # The tracing layer must be outermost, or it under-reports latency.\n",[618,1994,1995,2005,2020,2033,2052,2086,2104],{"__ignoreMap":816},[820,1996,1997,1999,2002],{"class":822,"line":823},[820,1998,907],{"class":833},[820,2000,2001],{"class":910}," test_middleware_registration_order",[820,2003,2004],{"class":837},"(client):\n",[820,2006,2007,2010,2012,2015,2018],{"class":822,"line":830},[820,2008,2009],{"class":837},"    resp ",[820,2011,888],{"class":833},[820,2013,2014],{"class":837}," client.get(",[820,2016,2017],{"class":826},"\"\u002Forder\u002F7\"",[820,2019,979],{"class":837},[820,2021,2022,2025,2028,2030],{"class":822,"line":847},[820,2023,2024],{"class":833},"    assert",[820,2026,2027],{"class":837}," resp.status_code ",[820,2029,1116],{"class":833},[820,2031,2032],{"class":875}," 200\n",[820,2034,2035,2038,2040,2042,2044,2047,2049],{"class":822,"line":854},[820,2036,2037],{"class":837},"    timeline ",[820,2039,888],{"class":833},[820,2041,2014],{"class":837},[820,2043,1729],{"class":826},[820,2045,2046],{"class":837},").json()[",[820,2048,1764],{"class":826},[820,2050,2051],{"class":837},"]\n",[820,2053,2054,2057,2059,2062,2065,2068,2071,2074,2077,2080,2083],{"class":822,"line":867},[820,2055,2056],{"class":837},"    entry ",[820,2058,888],{"class":833},[820,2060,2061],{"class":837}," [s ",[820,2063,2064],{"class":833},"for",[820,2066,2067],{"class":837}," s ",[820,2069,2070],{"class":833},"in",[820,2072,2073],{"class":837}," timeline ",[820,2075,2076],{"class":833},"if",[820,2078,2079],{"class":826}," \"BEFORE next layer\"",[820,2081,2082],{"class":833}," in",[820,2084,2085],{"class":837}," s]\n",[820,2087,2088,2090,2093,2096,2099,2102],{"class":822,"line":872},[820,2089,2024],{"class":833},[820,2091,2092],{"class":837}," entry[",[820,2094,2095],{"class":875},"0",[820,2097,2098],{"class":837},"].endswith(",[820,2100,2101],{"class":826},"\"outer: BEFORE next layer\"",[820,2103,979],{"class":837},[820,2105,2106],{"class":822,"line":894},[820,2107,2109],{"class":2108},"sFeEa","    # The tracing layer must be outermost, or it under-reports latency.\n",[590,2111,2112,2113,2115,2116,2119],{},"A test like this fails loudly the day somebody moves an ",[618,2114,1885],{}," call in ",[618,2117,2118],{},"create_app()",". That is worth having, because nothing else in your test suite will notice.",[753,2121,2123],{"id":2122},"trade-offs-and-when-not-to-reason-this-way","Trade-offs and when not to reason this way",[590,2125,2126],{},"Instrumenting the lifecycle is a debugging technique, not a design technique. Two cautions.",[590,2128,2129,2130,2133],{},"First, ",[593,2131,2132],{},"do not encode step ordering into business logic",". The stable, contractual guarantees are narrow: middleware nests according to registration order; dependencies resolve before the handler; serialization happens before middleware sees the response; background tasks run after the body is sent. Everything finer than that — especially the exact position of teardown relative to background tasks — is an implementation detail that has changed and may change again.",[590,2135,2136,2137,2140,2141,2143],{},"Second, ",[593,2138,2139],{},"this trace used pure ASGI middleware to obtain a clean result",". If your application uses ",[618,2142,627],{},", your own trace will look less tidy, with teardown interleaved among middleware exits, because the response is delivered through a stream rather than by return. That is not a bug in your instrumentation. It is a reason to prefer pure ASGI when ordering matters — and a reason not to write code that depends on the interleaving either way.",[590,2145,2146,2147,803],{},"Finally, the module-level list used here is not thread-safe or concurrency-safe. Under real load, multiple requests interleave and the list becomes meaningless. Trace one request at a time, or key the events by a request ID from ",[618,2148,2149],{},"contextvars",[753,2151,2153],{"id":2152},"faq","FAQ",[590,2155,2156,2162,2163,2165,2166,2168],{},[593,2157,2158,2159,2161],{},"Do background tasks run before or after ",[618,2160,620],{}," dependency teardown?","\nIn FastAPI 0.139.2 background tasks execute first, then ",[618,2164,620],{}," dependency teardown runs. Both happen after the response body has been fully sent to the client and before any middleware's post-call code resumes. A background task that needs a database session must therefore not rely on a session provided by a ",[618,2167,620],{}," dependency, because the ordering is an implementation detail rather than a documented contract.",[590,2170,2171,2177,2179,2180,2182,2183,2186],{},[593,2172,2173,2174,2176],{},"Why is the ordering messier with ",[618,2175,627],{}," than with pure ASGI middleware?",[618,2178,627],{}," runs the inner application in an AnyIO task group and pipes the response through a memory object stream so ",[618,2181,797],{}," can return a ",[618,2184,2185],{},"Response"," object. That indirection means the inner layers resume as soon as response headers are available rather than when the body is finished, so teardown can interleave between middleware exits.",[590,2188,2189,2192,2193,2195,2196,2198,2199,2201],{},[593,2190,2191],{},"Has my response already been serialized when middleware sees it?","\nYes. The trace shows the path operation returning a plain ",[618,2194,1911],{},", and the first thing any middleware observes afterwards is an ",[618,2197,1932],{}," message with a status code. Validation against ",[618,2200,769],{}," and JSON encoding both happened inside the router before the message was emitted.",[590,2203,2204,2207,2208,2210],{},[593,2205,2206],{},"Can I make the trace deterministic in my own application?","\nUse pure ASGI middleware rather than ",[618,2209,627],{}," wherever ordering matters. Pure ASGI middleware has no task group and no buffering, so its post-call code runs exactly when the inner application returns, which produces a strictly nested and reproducible order.",[753,2212,2214],{"id":2213},"related-reading","Related Reading",[597,2216,2217,2225,2232,2242,2251],{},[600,2218,2219,1879,2222,2224],{},[593,2220,2221],{},"Up to the guide:",[633,2223,557],{"href":635}," for the layer-by-layer model this trace confirms.",[600,2226,2227,1879,2230,803],{},[593,2228,2229],{},"The stage between step 07 and step 08:",[633,2231,569],{"href":1923},[600,2233,2234,1879,2237,2241],{},[593,2235,2236],{},"When there is no single body to serialize:",[633,2238,2240],{"href":2239},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002F","Streaming and File Responses"," changes the shape of steps 08 to 13 entirely.",[600,2243,2244,1879,2247,803],{},[593,2245,2246],{},"Ordering rules in practice:",[633,2248,2250],{"href":2249},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002F","Middleware Execution Order",[600,2252,2253,1879,2256,803],{},[593,2254,2255],{},"The teardown half:",[633,2257,1972],{"href":1971},[2259,2260,2261],"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 .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);}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}",{"title":816,"searchDepth":830,"depth":830,"links":2263},[2264,2265,2266,2267,2268,2269,2270,2271],{"id":755,"depth":830,"text":756},{"id":776,"depth":830,"text":777},{"id":1851,"depth":830,"text":1852},{"id":1872,"depth":830,"text":1873},{"id":1981,"depth":830,"text":1982},{"id":2122,"depth":830,"text":2123},{"id":2152,"depth":830,"text":2153},{"id":2213,"depth":830,"text":2214},"2026-07-20","Settle the which-runs-first question with a real traced FastAPI request: middleware, dependencies, the path operation, background tasks and teardown, in order.","md",[2276,2279,2282,2284],{"q":2277,"a":2278},"Do background tasks run before or after yield dependency teardown?","In FastAPI 0.139.2 background tasks execute first, then yield dependency teardown runs. Both happen after the response body has been fully sent to the client and before any middleware's post-call code resumes. A background task that needs a database session must therefore not rely on a session provided by a yield dependency, because the ordering is an implementation detail rather than a documented contract.",{"q":2280,"a":2281},"Why is the ordering messier with BaseHTTPMiddleware than with pure ASGI middleware?","BaseHTTPMiddleware runs the inner application in an AnyIO task group and pipes the response through a memory object stream so call_next can return a Response object. That indirection means the inner layers resume as soon as response headers are available rather than when the body is finished, so teardown can interleave between middleware exits.",{"q":2191,"a":2283},"Yes. The trace shows the path operation returning a plain dict, and the first thing any middleware observes afterwards is an http.response.start message with a status code. Validation against response_model and JSON encoding both happened inside the router before the message was emitted.",{"q":2206,"a":2285},"Use pure ASGI middleware rather than BaseHTTPMiddleware wherever ordering matters. Pure ASGI middleware has no task group and no buffering, so its post-call code runs exactly when the inner application returns, which produces a strictly nested and reproducible order.",null,{"slug":588,"breadcrumb":2288},[2289,2292,2295,2297],{"label":2290,"path":2291},"Home","\u002F",{"label":2293,"path":2294},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":2296,"path":635},"Request\u002FResponse Lifecycle",{"label":563,"path":2298},"\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002F",{"title":563,"description":2273},"article","EWDCQzX_Go_OzLtK3cobg7_W5bWng8TF6QxR6aWOqOM",[2286,2286],1784588202739]