[{"data":1,"prerenderedAt":1923},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F":580,"surround-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F":1922},[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":521,"body":582,"dateModified":1892,"datePublished":1892,"description":1893,"extension":1894,"faq":1895,"howto":1906,"meta":1907,"navigation":874,"path":522,"seo":1919,"stem":523,"type":1920,"__hash__":1921},"content\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex.md",{"type":583,"value":584,"toc":1881},"minimark",[585,589,596,632,641,646,649,652,770,774,793,800,803,828,832,835,1325,1328,1335,1338,1379,1383,1550,1561,1565,1576,1582,1596,1624,1628,1634,1733,1736,1740,1757,1763,1769,1779,1783,1789,1798,1807,1813,1819,1825,1829,1877],[586,587,521],"h1",{"id":588},"middleware-vs-dependencies-when-to-use-which",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,607,610,622,629],"ul",{},[600,601,602,603,606],"li",{},"Middleware wraps the router, so it runs for ",[593,604,605],{},"every"," request — 404s, mounted sub-applications, OPTIONS preflights.",[600,608,609],{},"Dependencies attach to routes, so they run only after a route has matched, and never for a 404.",[600,611,612,613,616,617,621],{},"Only a dependency can hand the endpoint a ",[593,614,615],{},"typed"," value; middleware can only stash untyped data on ",[618,619,620],"code",{},"request.state",".",[600,623,624,625,628],{},"Only a dependency appears in OpenAPI and can be replaced with ",[618,626,627],{},"dependency_overrides"," in tests.",[600,630,631],{},"Cross-cutting infrastructure (tracing, CORS, compression, timing) is middleware. Per-route policy (auth, tenancy, rate limits) is a dependency.",[590,633,634,635,640],{},"This guide sits under ",[636,637,639],"a",{"href":638},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","middleware implementation"," and answers the question people actually arrive with: they have a piece of cross-cutting logic and two plausible places to put it.",[642,643,645],"h2",{"id":644},"the-problem-this-solves","The Problem This Solves",[590,647,648],{},"Authentication, rate limiting, request logging, tenant resolution, feature flags — each of these could be written either way, and the two versions look about equally reasonable in a code review. The choice only reveals itself later: when a health check starts requiring an auth token, when a 404 disappears from your access log, when a test cannot stub out the tenant, or when the endpoint needs the value middleware computed and there is no type on it.",[590,650,651],{},"The distinction is not stylistic. It follows from where each hook sits in the ASGI stack.",[653,654,655,766],"figure",{},[656,657,665,666,665,670,665,674,665,683,665,690,665,695,665,703,665,709,665,713,665,721,665,725,665,729,665,737,665,743,665,749,665,755,665,760,665,763],"svg",{"viewBox":658,"role":659,"ariaLabelledBy":660,"xmlns":663,"style":664},"0 0 720 330","img",[661,662],"mvd-t","mvd-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[667,668,669],"title",{"id":661},"Where middleware and dependencies sit in the ASGI stack",[671,672,673],"desc",{"id":662},"Middleware wraps the router and therefore sees unmatched requests. The router dispatches to a route, and only then are dependencies resolved before the endpoint body runs.",[675,676],"rect",{"x":677,"y":678,"width":679,"height":680,"rx":681,"style":682},"30","20","660","230","10","fill:none;stroke:#00796B;stroke-width:1.8px",[684,685,689],"text",{"x":686,"y":687,"style":688},"52","44","text-anchor:start;fill:#00695C;font:700 13px sans-serif","Middleware stack",[684,691,694],{"x":686,"y":692,"style":693},"62","text-anchor:start;fill:#4B5563;font:400 11px sans-serif","runs for every request",[675,696],{"x":697,"y":698,"width":699,"height":700,"rx":701,"style":702},"70","78","580","150","9","fill:none;stroke:currentColor;stroke-width:1.5px",[684,704,708],{"x":705,"y":706,"style":707},"92","100","text-anchor:start;fill:currentColor;font:700 13px sans-serif","Router",[684,710,712],{"x":705,"y":711,"style":693},"118","no match here means 404",[675,714],{"x":715,"y":716,"width":717,"height":718,"rx":719,"style":720},"110","132","500","80","8","fill:none;stroke:#00796B;stroke-width:1.5px",[684,722,724],{"x":716,"y":723,"style":688},"154","Dependencies",[684,726,728],{"x":716,"y":727,"style":693},"172","matched routes only",[675,730],{"x":731,"y":732,"width":733,"height":734,"rx":735,"style":736},"380","146","210","48","7","fill:#E0F2F1;stroke:#00796B;stroke-width:1.5px",[684,738,742],{"x":739,"y":740,"style":741},"485","176","text-anchor:middle;fill:#00695C;font:700 13px sans-serif","endpoint body",[744,745],"line",{"x1":678,"y1":746,"x2":747,"y2":746,"style":748},"276","700","stroke:currentColor;stroke-width:1.2px",[684,750,754],{"x":751,"y":752,"style":753},"120","300","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","404 stops here",[684,756,759],{"x":751,"y":757,"style":758},"266","text-anchor:middle;fill:#4B5563;font:400 11px sans-serif","seen by middleware",[684,761,762],{"x":739,"y":752,"style":753},"typed value reaches the handler",[684,764,765],{"x":739,"y":757,"style":758},"seen by both",[767,768,769],"figcaption",{},"Middleware is outside routing; dependencies are inside it. Everything else follows from that.",[642,771,773],{"id":772},"why-it-happens-position-in-the-asgi-chain","Why It Happens: Position in the ASGI Chain",[590,775,776,777,780,781,784,785,789,790,792],{},"A FastAPI application is an ASGI callable wrapped in layers. Middleware you register with ",[618,778,779],{},"add_middleware"," or ",[618,782,783],{},"@app.middleware(\"http\")"," wraps the ",[786,787,788],"em",{},"entire"," application, including the ",[618,791,708],{}," object. When a request arrives, every middleware's inbound half runs before the router has looked at the path at all.",[590,794,795,796,799],{},"Dependencies are resolved by the route handler machinery, after the router has matched a path and a method. FastAPI builds a ",[618,797,798],{},"Dependant"," tree per route at startup and walks it per request. Nothing in that tree can run for a request that matched no route, because there is no route to own the tree.",[590,801,802],{},"Two consequences follow directly:",[597,804,805,817],{},[600,806,807,808,811,812,816],{},"Middleware sees requests that will 404, requests to mounted sub-applications, and ",[618,809,810],{},"OPTIONS"," preflights that ",[636,813,815],{"href":814},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002F","CORSMiddleware"," answers before any route is involved.",[600,818,819,820,823,824,827],{},"Dependencies can participate in the request's typed contract. A dependency's return value is a parameter of the endpoint function, so it is checked by your type checker and documented in OpenAPI. Middleware has no such channel; the best it can do is ",[618,821,822],{},"request.state.something",", which is ",[618,825,826],{},"Any"," forever.",[642,829,831],{"id":830},"proving-it-one-event-log-both-hooks","Proving It: One Event Log, Both Hooks",[590,833,834],{},"This app registers one middleware and one dependency that append to the same list, then makes requests that exercise the interesting cases — a matched route, a 404, and a mounted sub-application.",[836,837,842],"pre",{"className":838,"code":839,"language":840,"meta":841,"style":841},"language-python shiki shiki-themes github-light-high-contrast","\"\"\"Middleware sees every request (404s, mounts, static); dependencies see only matched routes.\"\"\"\nfrom typing import Annotated\n\nfrom fastapi import Depends, FastAPI, Request\n\napp = FastAPI()\n\nSEEN: list[str] = []\n\n\n@app.middleware(\"http\")\nasync def observer(request: Request, call_next):\n    response = await call_next(request)\n    SEEN.append(f\"middleware saw {request.method} {request.url.path} -> {response.status_code}\")\n    return response\n\n\nasync def route_gate(request: Request) -> str:\n    SEEN.append(f\"dependency saw {request.method} {request.url.path}\")\n    return \"gated\"\n\n\n@app.get(\"\u002Fmatched\", dependencies=[Depends(route_gate)])\nasync def matched():\n    return {\"ok\": True}\n\n\n@app.get(\"\u002Ftyped\")\nasync def typed(gate: Annotated[str, Depends(route_gate)]):\n    # A dependency can hand a typed value to the handler. Middleware cannot.\n    return {\"value_from_dependency\": gate}\n\n\nsub = FastAPI()\n\n\n@sub.get(\"\u002Fhealth\")\nasync def sub_health():\n    return {\"sub\": \"ok\"}\n\n\napp.mount(\"\u002Fsub\", sub)\n","python","",[618,843,844,852,869,876,889,894,906,911,932,937,942,958,973,987,1034,1043,1048,1053,1071,1099,1107,1112,1117,1140,1153,1172,1177,1182,1194,1212,1219,1232,1237,1242,1252,1257,1262,1275,1287,1303,1308,1313],{"__ignoreMap":841},[845,846,848],"span",{"class":744,"line":847},1,[845,849,851],{"class":850},"sYEJz","\"\"\"Middleware sees every request (404s, mounts, static); dependencies see only matched routes.\"\"\"\n",[845,853,855,859,863,866],{"class":744,"line":854},2,[845,856,858],{"class":857},"sTJeM","from",[845,860,862],{"class":861},"sigWx"," typing ",[845,864,865],{"class":857},"import",[845,867,868],{"class":861}," Annotated\n",[845,870,872],{"class":744,"line":871},3,[845,873,875],{"emptyLinePlaceholder":874},true,"\n",[845,877,879,881,884,886],{"class":744,"line":878},4,[845,880,858],{"class":857},[845,882,883],{"class":861}," fastapi ",[845,885,865],{"class":857},[845,887,888],{"class":861}," Depends, FastAPI, Request\n",[845,890,892],{"class":744,"line":891},5,[845,893,875],{"emptyLinePlaceholder":874},[845,895,897,900,903],{"class":744,"line":896},6,[845,898,899],{"class":861},"app ",[845,901,902],{"class":857},"=",[845,904,905],{"class":861}," FastAPI()\n",[845,907,909],{"class":744,"line":908},7,[845,910,875],{"emptyLinePlaceholder":874},[845,912,914,918,921,924,927,929],{"class":744,"line":913},8,[845,915,917],{"class":916},"sacAq","SEEN",[845,919,920],{"class":861},": list[",[845,922,923],{"class":916},"str",[845,925,926],{"class":861},"] ",[845,928,902],{"class":857},[845,930,931],{"class":861}," []\n",[845,933,935],{"class":744,"line":934},9,[845,936,875],{"emptyLinePlaceholder":874},[845,938,940],{"class":744,"line":939},10,[845,941,875],{"emptyLinePlaceholder":874},[845,943,945,949,952,955],{"class":744,"line":944},11,[845,946,948],{"class":947},"s3dhs","@app.middleware",[845,950,951],{"class":861},"(",[845,953,954],{"class":850},"\"http\"",[845,956,957],{"class":861},")\n",[845,959,961,964,967,970],{"class":744,"line":960},12,[845,962,963],{"class":857},"async",[845,965,966],{"class":857}," def",[845,968,969],{"class":947}," observer",[845,971,972],{"class":861},"(request: Request, call_next):\n",[845,974,976,979,981,984],{"class":744,"line":975},13,[845,977,978],{"class":861},"    response ",[845,980,902],{"class":857},[845,982,983],{"class":857}," await",[845,985,986],{"class":861}," call_next(request)\n",[845,988,990,993,996,999,1002,1005,1008,1011,1014,1017,1019,1022,1024,1027,1029,1032],{"class":744,"line":989},14,[845,991,992],{"class":916},"    SEEN",[845,994,995],{"class":861},".append(",[845,997,998],{"class":857},"f",[845,1000,1001],{"class":850},"\"middleware saw ",[845,1003,1004],{"class":857},"{",[845,1006,1007],{"class":861},"request.method",[845,1009,1010],{"class":857},"}",[845,1012,1013],{"class":857}," {",[845,1015,1016],{"class":861},"request.url.path",[845,1018,1010],{"class":857},[845,1020,1021],{"class":850}," -> ",[845,1023,1004],{"class":857},[845,1025,1026],{"class":861},"response.status_code",[845,1028,1010],{"class":857},[845,1030,1031],{"class":850},"\"",[845,1033,957],{"class":861},[845,1035,1037,1040],{"class":744,"line":1036},15,[845,1038,1039],{"class":857},"    return",[845,1041,1042],{"class":861}," response\n",[845,1044,1046],{"class":744,"line":1045},16,[845,1047,875],{"emptyLinePlaceholder":874},[845,1049,1051],{"class":744,"line":1050},17,[845,1052,875],{"emptyLinePlaceholder":874},[845,1054,1056,1058,1060,1063,1066,1068],{"class":744,"line":1055},18,[845,1057,963],{"class":857},[845,1059,966],{"class":857},[845,1061,1062],{"class":947}," route_gate",[845,1064,1065],{"class":861},"(request: Request) -> ",[845,1067,923],{"class":916},[845,1069,1070],{"class":861},":\n",[845,1072,1074,1076,1078,1080,1083,1085,1087,1089,1091,1093,1095,1097],{"class":744,"line":1073},19,[845,1075,992],{"class":916},[845,1077,995],{"class":861},[845,1079,998],{"class":857},[845,1081,1082],{"class":850},"\"dependency saw ",[845,1084,1004],{"class":857},[845,1086,1007],{"class":861},[845,1088,1010],{"class":857},[845,1090,1013],{"class":857},[845,1092,1016],{"class":861},[845,1094,1010],{"class":857},[845,1096,1031],{"class":850},[845,1098,957],{"class":861},[845,1100,1102,1104],{"class":744,"line":1101},20,[845,1103,1039],{"class":857},[845,1105,1106],{"class":850}," \"gated\"\n",[845,1108,1110],{"class":744,"line":1109},21,[845,1111,875],{"emptyLinePlaceholder":874},[845,1113,1115],{"class":744,"line":1114},22,[845,1116,875],{"emptyLinePlaceholder":874},[845,1118,1120,1123,1125,1128,1131,1135,1137],{"class":744,"line":1119},23,[845,1121,1122],{"class":947},"@app.get",[845,1124,951],{"class":861},[845,1126,1127],{"class":850},"\"\u002Fmatched\"",[845,1129,1130],{"class":861},", ",[845,1132,1134],{"class":1133},"sV4o_","dependencies",[845,1136,902],{"class":857},[845,1138,1139],{"class":861},"[Depends(route_gate)])\n",[845,1141,1143,1145,1147,1150],{"class":744,"line":1142},24,[845,1144,963],{"class":857},[845,1146,966],{"class":857},[845,1148,1149],{"class":947}," matched",[845,1151,1152],{"class":861},"():\n",[845,1154,1156,1158,1160,1163,1166,1169],{"class":744,"line":1155},25,[845,1157,1039],{"class":857},[845,1159,1013],{"class":861},[845,1161,1162],{"class":850},"\"ok\"",[845,1164,1165],{"class":861},": ",[845,1167,1168],{"class":916},"True",[845,1170,1171],{"class":861},"}\n",[845,1173,1175],{"class":744,"line":1174},26,[845,1176,875],{"emptyLinePlaceholder":874},[845,1178,1180],{"class":744,"line":1179},27,[845,1181,875],{"emptyLinePlaceholder":874},[845,1183,1185,1187,1189,1192],{"class":744,"line":1184},28,[845,1186,1122],{"class":947},[845,1188,951],{"class":861},[845,1190,1191],{"class":850},"\"\u002Ftyped\"",[845,1193,957],{"class":861},[845,1195,1197,1199,1201,1204,1207,1209],{"class":744,"line":1196},29,[845,1198,963],{"class":857},[845,1200,966],{"class":857},[845,1202,1203],{"class":947}," typed",[845,1205,1206],{"class":861},"(gate: Annotated[",[845,1208,923],{"class":916},[845,1210,1211],{"class":861},", Depends(route_gate)]):\n",[845,1213,1215],{"class":744,"line":1214},30,[845,1216,1218],{"class":1217},"sFeEa","    # A dependency can hand a typed value to the handler. Middleware cannot.\n",[845,1220,1222,1224,1226,1229],{"class":744,"line":1221},31,[845,1223,1039],{"class":857},[845,1225,1013],{"class":861},[845,1227,1228],{"class":850},"\"value_from_dependency\"",[845,1230,1231],{"class":861},": gate}\n",[845,1233,1235],{"class":744,"line":1234},32,[845,1236,875],{"emptyLinePlaceholder":874},[845,1238,1240],{"class":744,"line":1239},33,[845,1241,875],{"emptyLinePlaceholder":874},[845,1243,1245,1248,1250],{"class":744,"line":1244},34,[845,1246,1247],{"class":861},"sub ",[845,1249,902],{"class":857},[845,1251,905],{"class":861},[845,1253,1255],{"class":744,"line":1254},35,[845,1256,875],{"emptyLinePlaceholder":874},[845,1258,1260],{"class":744,"line":1259},36,[845,1261,875],{"emptyLinePlaceholder":874},[845,1263,1265,1268,1270,1273],{"class":744,"line":1264},37,[845,1266,1267],{"class":947},"@sub.get",[845,1269,951],{"class":861},[845,1271,1272],{"class":850},"\"\u002Fhealth\"",[845,1274,957],{"class":861},[845,1276,1278,1280,1282,1285],{"class":744,"line":1277},38,[845,1279,963],{"class":857},[845,1281,966],{"class":857},[845,1283,1284],{"class":947}," sub_health",[845,1286,1152],{"class":861},[845,1288,1290,1292,1294,1297,1299,1301],{"class":744,"line":1289},39,[845,1291,1039],{"class":857},[845,1293,1013],{"class":861},[845,1295,1296],{"class":850},"\"sub\"",[845,1298,1165],{"class":861},[845,1300,1162],{"class":850},[845,1302,1171],{"class":861},[845,1304,1306],{"class":744,"line":1305},40,[845,1307,875],{"emptyLinePlaceholder":874},[845,1309,1311],{"class":744,"line":1310},41,[845,1312,875],{"emptyLinePlaceholder":874},[845,1314,1316,1319,1322],{"class":744,"line":1315},42,[845,1317,1318],{"class":861},"app.mount(",[845,1320,1321],{"class":850},"\"\u002Fsub\"",[845,1323,1324],{"class":861},", sub)\n",[590,1326,1327],{},"Real output from the verification run:",[836,1329,1333],{"className":1330,"code":1332,"language":684,"meta":841},[1331],"language-text","$ GET \u002Fmatched\n200 OK\n{\n  \"ok\": true\n}\n\n$ GET \u002Ftyped\n200 OK\n{\n  \"value_from_dependency\": \"gated\"\n}\n\n$ GET \u002Fno-such-route\n404 Not Found\n{\n  \"detail\": \"Not Found\"\n}\n\n$ GET \u002Fsub\u002Fhealth\n200 OK\n{\n  \"sub\": \"ok\"\n}\n\n$ GET \u002Fseen\n200 OK\n{\n  \"seen\": [\n    \"middleware saw POST \u002Freset -> 200\",\n    \"dependency saw GET \u002Fmatched\",\n    \"middleware saw GET \u002Fmatched -> 200\",\n    \"dependency saw GET \u002Ftyped\",\n    \"middleware saw GET \u002Ftyped -> 200\",\n    \"middleware saw GET \u002Fno-such-route -> 404\",\n    \"middleware saw GET \u002Fsub\u002Fhealth -> 200\"\n  ]\n}\n",[618,1334,1332],{"__ignoreMap":841},[590,1336,1337],{},"Every line matters:",[597,1339,1340,1358,1364,1370],{},[600,1341,1342,1343,1346,1347,1350,1351,1354,1355,621],{},"For ",[618,1344,1345],{},"\u002Fmatched"," and ",[618,1348,1349],{},"\u002Ftyped",", both hooks fired, and the dependency fired ",[786,1352,1353],{},"first"," — it runs inside the middleware's ",[618,1356,1357],{},"call_next",[600,1359,1342,1360,1363],{},[618,1361,1362],{},"\u002Fno-such-route",", only the middleware line exists. The 404 never reached a route, so no dependency could run. If your access log or your request-count metric is built on a dependency, every 404 in production is invisible to it.",[600,1365,1342,1366,1369],{},[618,1367,1368],{},"\u002Fsub\u002Fhealth",", the outer middleware saw the request even though the route belongs to a mounted sub-application with its own routing table. Dependencies declared on the outer app do not apply there at all.",[600,1371,1372,1374,1375,1378],{},[618,1373,1349],{}," returned ",[618,1376,1377],{},"\"gated\""," in its body — a value produced by the dependency and received by the handler as a typed parameter. There is no middleware equivalent of that line.",[642,1380,1382],{"id":1381},"the-decision-table","The Decision Table",[1384,1385,1386,1401],"table",{},[1387,1388,1389],"thead",{},[1390,1391,1392,1395,1398],"tr",{},[1393,1394],"th",{},[1393,1396,1397],{},"Middleware",[1393,1399,1400],{},"Dependency",[1402,1403,1404,1416,1426,1439,1452,1462,1475,1486,1502,1513,1523,1539],"tbody",{},[1390,1405,1406,1410,1413],{},[1407,1408,1409],"td",{},"Runs for unmatched routes (404)",[1407,1411,1412],{},"Yes",[1407,1414,1415],{},"No",[1390,1417,1418,1421,1423],{},[1407,1419,1420],{},"Runs for mounted sub-apps",[1407,1422,1412],{},[1407,1424,1425],{},"Only if declared on that sub-app",[1390,1427,1428,1434,1436],{},[1407,1429,1430,1431,1433],{},"Runs for ",[618,1432,810],{}," preflight",[1407,1435,1412],{},[1407,1437,1438],{},"No — answered before routing",[1390,1440,1441,1444,1449],{},[1407,1442,1443],{},"Can return a typed value to the handler",[1407,1445,1446,1447],{},"No, only ",[618,1448,620],{},[1407,1450,1451],{},"Yes, as a declared parameter",[1390,1453,1454,1457,1459],{},[1407,1455,1456],{},"Appears in the OpenAPI schema",[1407,1458,1415],{},[1407,1460,1461],{},"Yes, including security schemes",[1390,1463,1464,1467,1470],{},[1407,1465,1466],{},"Replaceable in tests",[1407,1468,1469],{},"Rebuild the app",[1407,1471,1472],{},[618,1473,1474],{},"app.dependency_overrides",[1390,1476,1477,1480,1483],{},[1407,1478,1479],{},"Can be scoped to some routes",[1407,1481,1482],{},"Only by inspecting the path",[1407,1484,1485],{},"Natively, per route or per router",[1390,1487,1488,1491,1496],{},[1407,1489,1490],{},"Can short-circuit with a response",[1407,1492,1493,1494],{},"Yes, by not calling ",[618,1495,1357],{},[1407,1497,1498,1499],{},"Yes, by raising ",[618,1500,1501],{},"HTTPException",[1390,1503,1504,1507,1510],{},[1407,1505,1506],{},"Can modify the outgoing response",[1407,1508,1509],{},"Yes — headers, body, status",[1407,1511,1512],{},"Only via the exception path",[1390,1514,1515,1518,1520],{},[1407,1516,1517],{},"Sees the response status code",[1407,1519,1412],{},[1407,1521,1522],{},"Not directly",[1390,1524,1525,1528,1533],{},[1407,1526,1527],{},"Has per-request setup and teardown",[1407,1529,1530,1531],{},"Yes, around ",[618,1532,1357],{},[1407,1534,1535,1536],{},"Yes, with ",[618,1537,1538],{},"yield",[1390,1540,1541,1544,1547],{},[1407,1542,1543],{},"Ordering model",[1407,1545,1546],{},"Stack; last added is outermost",[1407,1548,1549],{},"Declaration order within a route",[590,1551,1552,1553,1556,1557,1560],{},"Read the table as two clusters. The middleware column is about ",[786,1554,1555],{},"the transport",": every byte in and out, regardless of what the application does with it. The dependency column is about ",[786,1558,1559],{},"the contract",": what this particular operation requires in order to run.",[642,1562,1564],{"id":1563},"choosing-in-practice","Choosing in Practice",[590,1566,1567,1570,1571,1575],{},[593,1568,1569],{},"Use middleware for:"," correlation IDs and ",[636,1572,1574],{"href":1573},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F","request tracing",", CORS, GZip, access logging, total-request metrics, wall-clock timing, and anything that must also cover 404s and malformed paths. These are properties of the HTTP conversation, not of any endpoint.",[590,1577,1578,1581],{},[593,1579,1580],{},"Use a dependency for:"," authentication and authorization, tenant resolution, pagination parameters, per-route rate limits, feature-flag gates, database sessions. These are inputs the endpoint needs, and modelling them as inputs is what keeps them typed, documented and testable.",[590,1583,1584,1587,1588,1591,1592,1595],{},[593,1585,1586],{},"Use both when context flows one way."," The strongest pattern in practice: middleware sets a request ID into a ",[618,1589,1590],{},"contextvar"," for every request, and a dependency reads it and returns a typed ",[618,1593,1594],{},"RequestContext"," for the routes that want one. Neither layer is doing the other's job.",[590,1597,1598,1599,1602,1603,1606,1607,1610,1611,1614,1615,1618,1619,1623],{},"Authentication deserves the explicit argument, because middleware auth is the single most common misplacement. As middleware it runs on ",[618,1600,1601],{},"\u002Fhealth",", on ",[618,1604,1605],{},"\u002Fdocs",", and on ",[618,1608,1609],{},"\u002Fopenapi.json"," unless you maintain a path allowlist — and that allowlist is a second source of truth that drifts from your router. It cannot express \"this route needs the ",[618,1612,1613],{},"admin"," scope\". It cannot be overridden per test. And the user it computed arrives in the handler as ",[618,1616,1617],{},"request.state.user",", untyped. As a dependency, all four problems disappear, and router-level attachment described in ",[636,1620,1622],{"href":1621},"\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002F","modular router organization"," still lets you apply it to a whole group of routes in one line.",[642,1625,1627],{"id":1626},"verification","Verification",[590,1629,1630,1631,1633],{},"To find out which one your logic currently is, and whether it fires when you expect, add a temporary counter and hit the boundary cases — a 404, an ",[618,1632,810],{},", and your health check:",[836,1635,1637],{"className":838,"code":1636,"language":840,"meta":841,"style":841},"def test_auth_does_not_gate_health(client):\n    assert client.get(\"\u002Fhealth\").status_code == 200      # fails if auth is middleware\n    assert client.get(\"\u002Fnope\").status_code == 404        # a 404, not a 401\n\n\ndef test_metrics_count_404s(client, metrics):\n    client.get(\"\u002Fnope\")\n    assert metrics.requests_total == 1                   # fails if counting in a dependency\n",[618,1638,1639,1650,1672,1691,1695,1699,1709,1718],{"__ignoreMap":841},[845,1640,1641,1644,1647],{"class":744,"line":847},[845,1642,1643],{"class":857},"def",[845,1645,1646],{"class":947}," test_auth_does_not_gate_health",[845,1648,1649],{"class":861},"(client):\n",[845,1651,1652,1655,1658,1660,1663,1666,1669],{"class":744,"line":854},[845,1653,1654],{"class":857},"    assert",[845,1656,1657],{"class":861}," client.get(",[845,1659,1272],{"class":850},[845,1661,1662],{"class":861},").status_code ",[845,1664,1665],{"class":857},"==",[845,1667,1668],{"class":916}," 200",[845,1670,1671],{"class":1217},"      # fails if auth is middleware\n",[845,1673,1674,1676,1678,1681,1683,1685,1688],{"class":744,"line":871},[845,1675,1654],{"class":857},[845,1677,1657],{"class":861},[845,1679,1680],{"class":850},"\"\u002Fnope\"",[845,1682,1662],{"class":861},[845,1684,1665],{"class":857},[845,1686,1687],{"class":916}," 404",[845,1689,1690],{"class":1217},"        # a 404, not a 401\n",[845,1692,1693],{"class":744,"line":878},[845,1694,875],{"emptyLinePlaceholder":874},[845,1696,1697],{"class":744,"line":891},[845,1698,875],{"emptyLinePlaceholder":874},[845,1700,1701,1703,1706],{"class":744,"line":896},[845,1702,1643],{"class":857},[845,1704,1705],{"class":947}," test_metrics_count_404s",[845,1707,1708],{"class":861},"(client, metrics):\n",[845,1710,1711,1714,1716],{"class":744,"line":908},[845,1712,1713],{"class":861},"    client.get(",[845,1715,1680],{"class":850},[845,1717,957],{"class":861},[845,1719,1720,1722,1725,1727,1730],{"class":744,"line":913},[845,1721,1654],{"class":857},[845,1723,1724],{"class":861}," metrics.requests_total ",[845,1726,1665],{"class":857},[845,1728,1729],{"class":916}," 1",[845,1731,1732],{"class":1217},"                   # fails if counting in a dependency\n",[590,1734,1735],{},"Those two tests encode the whole decision, and they are worth keeping.",[642,1737,1739],{"id":1738},"trade-offs-and-when-not-to","Trade-Offs and When Not To",[590,1741,1742,1748,1749,1751,1752,1756],{},[593,1743,1744,1747],{},[618,1745,1746],{},"BaseHTTPMiddleware"," is not free."," It runs the downstream app in a separate task and streams the response through a queue, which adds overhead per request and changes when ",[618,1750,1538],{}," dependency teardown is observable, as shown in ",[636,1753,1755],{"href":1754},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F","yield dependencies and cleanup order",". For hot paths, pure ASGI middleware avoids that.",[590,1758,1759,1762],{},[593,1760,1761],{},"Middleware cannot read the parsed body cheaply."," Consuming the request stream in middleware to inspect JSON means buffering it and making it re-readable downstream. A dependency gets the validated Pydantic model for free.",[590,1764,1765,1768],{},[593,1766,1767],{},"Dependencies cannot see the response."," If you need the status code or the response size — for logging or metrics — you are in middleware territory, because the dependency has finished long before the response exists.",[590,1770,1771,1774,1775,621],{},[593,1772,1773],{},"Exception handlers sit between the two."," An exception raised in a dependency is converted to a response by FastAPI's handlers and then travels outward through the middleware stack; an exception raised in middleware is not. That asymmetry is developed in ",[636,1776,1778],{"href":1777},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002F","middleware execution order",[642,1780,1782],{"id":1781},"faq","FAQ",[590,1784,1785,1788],{},[593,1786,1787],{},"Does middleware run for requests that 404?","\nYes. Middleware wraps the router, so it runs before routing has happened and sees every request including ones that match no route at all. A dependency is attached to a route, so a 404 never reaches it.",[590,1790,1791,1794,1795,1797],{},[593,1792,1793],{},"Can middleware pass a value to my endpoint?","\nOnly untyped, through ",[618,1796,620],{},". A dependency returns a real typed value that the endpoint declares in its signature, which the type checker and the OpenAPI schema both understand. That is the strongest argument for using a dependency where you have the choice.",[590,1799,1800,1803,1804,1806],{},[593,1801,1802],{},"Which is faster, middleware or a dependency?","\nA dependency does less work per request than ",[618,1805,1746],{},", which wraps the response in an extra task and stream. For pure ASGI middleware the difference is small. The bigger cost is running logic on every request when only some routes need it.",[590,1808,1809,1812],{},[593,1810,1811],{},"Should authentication be middleware or a dependency?","\nA dependency, in almost every case. It can return the authenticated user as a typed value, it appears in the OpenAPI security schema, it can be overridden in tests, and it does not run on your health check or your docs route.",[590,1814,1815,1818],{},[593,1816,1817],{},"Can I use both for the same concern?","\nYes, and it is often the right shape. Middleware establishes ambient context such as a request ID for every request, and a dependency consumes that context for the routes that need it as a typed value.",[590,1820,1821,1824],{},[593,1822,1823],{},"Why does my dependency not run for a mounted sub-application?","\nA mount is a separate ASGI application with its own routing table and its own dependency declarations. Outer middleware still wraps it, but outer dependencies belong to the outer app's routes and have nothing to attach to inside the mount.",[642,1826,1828],{"id":1827},"related-reading","Related Reading",[597,1830,1831,1840,1849,1856,1864],{},[600,1832,1833,1836,1837,621],{},[593,1834,1835],{},"Up to the section:"," ",[636,1838,1839],{"href":638},"Middleware Implementation",[600,1841,1842,1836,1845,1848],{},[593,1843,1844],{},"When both layers are in play:",[636,1846,1847],{"href":1777},"Middleware Execution Order"," for how the stack unwinds and where exception handlers sit.",[600,1850,1851,1836,1854,621],{},[593,1852,1853],{},"The canonical middleware job:",[636,1855,509],{"href":1573},[600,1857,1858,1836,1861,621],{},[593,1859,1860],{},"A middleware you should not write yourself:",[636,1862,1863],{"href":814},"CORS Middleware Configuration",[600,1865,1866,1836,1869,1346,1873,621],{},[593,1867,1868],{},"The dependency side:",[636,1870,1872],{"href":1871},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","Dependency Injection Strategies",[636,1874,1876],{"href":1875},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","Overriding Dependencies in Tests",[1878,1879,1880],"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 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":841,"searchDepth":854,"depth":854,"links":1882},[1883,1884,1885,1886,1887,1888,1889,1890,1891],{"id":644,"depth":854,"text":645},{"id":772,"depth":854,"text":773},{"id":830,"depth":854,"text":831},{"id":1381,"depth":854,"text":1382},{"id":1563,"depth":854,"text":1564},{"id":1626,"depth":854,"text":1627},{"id":1738,"depth":854,"text":1739},{"id":1781,"depth":854,"text":1782},{"id":1827,"depth":854,"text":1828},"2026-07-20","Middleware sees every request including 404s and mounted apps; dependencies see matched routes but return typed values. A decision guide with a real log.","md",[1896,1898,1900,1902,1904],{"q":1787,"a":1897},"Yes. Middleware wraps the router, so it runs before routing has happened and sees every request including ones that match no route at all. A dependency is attached to a route, so a 404 never reaches it.",{"q":1793,"a":1899},"Only untyped, through request.state. A dependency returns a real typed value that the endpoint declares in its signature, which the type checker and the OpenAPI schema both understand. That is the strongest argument for using a dependency where you have the choice.",{"q":1802,"a":1901},"A dependency does less work per request than BaseHTTPMiddleware, which wraps the response in an extra task and stream. For pure ASGI middleware the difference is small. The bigger cost is running logic on every request when only some routes need it.",{"q":1811,"a":1903},"A dependency, in almost every case. It can return the authenticated user as a typed value, it appears in the OpenAPI security schema, it can be overridden in tests, and it does not run on your health check or your docs route.",{"q":1817,"a":1905},"Yes, and it is often the right shape. Middleware establishes ambient context such as a request ID for every request, and a dependency consumes that context for the routes that need it as a typed value.",null,{"slug":588,"breadcrumb":1908},[1909,1912,1915,1916],{"label":1910,"path":1911},"Home","\u002F",{"label":1913,"path":1914},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":1839,"path":638},{"label":1917,"path":1918},"Middleware vs Dependencies","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F",{"title":521,"description":1893},"article","LeZDNm7Haa4Yvb4WmcXGTogJpG1RK4q0C3Z5TYHLYrU",[1906,1906],1784588202739]