[{"data":1,"prerenderedAt":2440},["ShallowReactive",2],{"nav":3,"page-\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002F":580,"surround-\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002F":2439},[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":181,"body":582,"dateModified":2403,"datePublished":2403,"description":2404,"extension":2405,"faq":2406,"howto":2421,"meta":2422,"navigation":978,"path":182,"seo":2436,"stem":183,"type":2437,"__hash__":2438},"content\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex.md",{"type":583,"value":584,"toc":2389},"minimark",[585,589,596,630,647,666,820,825,844,847,885,891,898,943,949,1296,1314,1319,1496,1499,1506,1511,1515,1518,1806,1809,1815,1838,1842,1845,1873,1876,1901,1916,1922,1934,1940,1944,1950,2000,2006,2012,2033,2040,2043,2142,2154,2158,2166,2185,2208,2222,2226,2235,2241,2251,2274,2278,2296,2311,2329,2335,2344,2348,2385],[586,587,181],"h1",{"id":588},"annotated-dependencies-and-reusable-types-in-fastapi",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,607,614,620,627],"ul",{},[600,601,602,606],"li",{},[603,604,605],"code",{},"Annotated[User, Depends(get_current_user)]"," can be aliased as a type and reused as a plain hint on every route.",[600,608,609,610,613],{},"Aliases work for constrained parameters too: ",[603,611,612],{},"Annotated[str, Query(min_length=2)]"," becomes a named contract.",[600,615,616,619],{},[603,617,618],{},"dependency_overrides"," keys off the underlying callable, so one override covers every route using the alias.",[600,621,622,623,626],{},"The default-value form breaks direct calls in tests and misleads type checkers; the ",[603,624,625],{},"Annotated"," form does not.",[600,628,629],{},"Aliases compose: dependencies build on dependencies, and each layer stays a one-word annotation.",[590,631,632,633,638,639,641,642,646],{},"This page sits under ",[634,635,637],"a",{"href":636},"\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002F","Type Hinting and IDE Integration"," and takes the ",[603,640,625],{}," mechanics introduced in ",[634,643,645],{"href":644},"\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002F","Request Validation Patterns"," to their conclusion: declarations you write once and use as ordinary type hints everywhere.",[590,648,649,650,653,654,657,658,661,662,665],{},"All output below is real, produced by ",[603,651,652],{},"_verify\u002Fexamples\u002Fval-annotated-deps.py"," on FastAPI 0.139.2, Pydantic 2.13.4 and Python 3.12. Because the harness cannot attach an ",[603,655,656],{},"Authorization"," header to its requests, that example drives the header-bearing and override cases from a ",[603,659,660],{},"\u002Fselftest"," endpoint that calls the same app in-process through httpx's ",[603,663,664],{},"ASGITransport"," and reports what genuinely came back.",[667,668,669,816],"figure",{},[670,671,679,680,679,684,679,688,679,697,679,704,679,707,679,711,679,714,679,718,679,723,679,728,679,731,679,735,679,740,679,745,679,747,679,750,679,755,679,760,679,763,679,768,679,776,679,779,679,782,679,787,679,789,679,792,679,797,679,801,679,806,679,812],"svg",{"viewBox":672,"role":673,"ariaLabelledBy":674,"xmlns":677,"style":678},"0 0 720 310","img",[675,676],"ann-title","ann-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[681,682,683],"title",{"id":675},"A dependency alias resolving through its layers",[685,686,687],"desc",{"id":676},"Three routes annotate a parameter with the alias AdminUser or CurrentUser. Both resolve to require_admin and get_current_user, which in turn depend on get_session and the Authorization header. A dependency_overrides entry replaces get_current_user for every route at once.",[689,690],"rect",{"x":691,"y":692,"width":693,"height":694,"rx":695,"style":696},"16","30","150","40","7","fill:#FFFFFF;stroke:currentColor;stroke-width:1.4px",[698,699,703],"text",{"x":700,"y":701,"style":702},"91","55","text-anchor:middle;fill:currentColor;font:600 12px monospace","GET \u002Fme",[689,705],{"x":691,"y":706,"width":693,"height":694,"rx":695,"style":696},"86",[698,708,710],{"x":700,"y":709,"style":702},"111","GET \u002Forders",[689,712],{"x":691,"y":713,"width":693,"height":694,"rx":695,"style":696},"142",[698,715,717],{"x":700,"y":716,"style":702},"167","GET \u002Fadmin\u002Faudit",[689,719],{"x":720,"y":713,"width":721,"height":694,"rx":695,"style":722},"210","160","fill:#E0F2F1;stroke:#00796B;stroke-width:1.6px",[698,724,727],{"x":725,"y":716,"style":726},"290","text-anchor:middle;fill:#00796B;font:700 12px monospace","AdminUser",[689,729],{"x":720,"y":730,"width":721,"height":694,"rx":695,"style":722},"58",[698,732,734],{"x":725,"y":733,"style":726},"83","CurrentUser",[689,736],{"x":737,"y":730,"width":738,"height":694,"rx":695,"style":739},"418","180","fill:#FFFFFF;stroke:#00796B;stroke-width:1.5px",[698,741,744],{"x":742,"y":733,"style":743},"508","text-anchor:middle;fill:#00796B;font:600 12px monospace","get_current_user",[689,746],{"x":737,"y":713,"width":738,"height":694,"rx":695,"style":739},[698,748,749],{"x":742,"y":716,"style":743},"require_admin",[689,751],{"x":737,"y":752,"width":738,"height":753,"rx":695,"style":754},"4","34","fill:#F9FAFB;stroke:currentColor;stroke-width:1.3px",[698,756,759],{"x":742,"y":757,"style":758},"26","text-anchor:middle;fill:currentColor;font:400 11.5px monospace","get_session",[689,761],{"x":737,"y":762,"width":738,"height":753,"rx":695,"style":754},"200",[698,764,767],{"x":742,"y":765,"style":766},"222","text-anchor:middle;fill:currentColor;font:400 11.5px sans-serif","Authorization header",[769,770],"line",{"x1":771,"y1":772,"x2":773,"y2":774,"style":775},"166","50","206","72","stroke:#00796B;stroke-width:1.4px",[769,777],{"x1":771,"y1":778,"x2":773,"y2":706,"style":775},"106",[769,780],{"x1":771,"y1":781,"x2":773,"y2":781,"style":775},"162",[769,783],{"x1":784,"y1":785,"x2":786,"y2":785,"style":775},"370","78","414",[769,788],{"x1":784,"y1":781,"x2":786,"y2":781,"style":775},[769,790],{"x1":742,"y1":713,"x2":742,"y2":791,"style":775},"102",[793,794],"polygon",{"points":795,"style":796},"504,106 512,106 508,98","fill:#00796B",[769,798],{"x1":742,"y1":730,"x2":742,"y2":799,"style":800},"42","stroke:currentColor;stroke-width:1.3px",[769,802],{"x1":742,"y1":803,"x2":742,"y2":804,"style":805},"98","196","stroke:currentColor;stroke-width:1.1px;stroke-dasharray:3 3",[698,807,811],{"x":808,"y":809,"style":810},"360","270","text-anchor:middle;fill:currentColor;font:400 12px sans-serif","dependency_overrides keys off the callables on the right, so one entry",[698,813,815],{"x":808,"y":814,"style":810},"288","replaces the dependency for every route using either alias.",[817,818,819],"figcaption",{},"The alias is only a name for the Annotated pair. Routes annotate with the alias; overrides target the callable it wraps.",[821,822,824],"h2",{"id":823},"the-problem-this-solves","The Problem This Solves",[590,826,827,828,831,832,835,836,839,840,843],{},"A mature FastAPI service ends up with the same six words on hundreds of parameters: ",[603,829,830],{},"user: User = Depends(get_current_user)",". Change the dependency's name and you touch every route. Add a session parameter to it and every call site is unaffected — good — but the type of ",[603,833,834],{},"user"," was never checked by anything, and a test that calls the handler directly receives a ",[603,837,838],{},"Depends"," object where it expected a ",[603,841,842],{},"User",".",[590,845,846],{},"Aliasing the whole declaration turns it into a single word:",[848,849,854],"pre",{"className":850,"code":851,"language":852,"meta":853,"style":853},"language-python shiki shiki-themes github-light-high-contrast","async def me(user: CurrentUser) -> dict[str, Any]: ...\n","python","",[603,855,856],{"__ignoreMap":853},[857,858,860,864,867,871,875,879,882],"span",{"class":769,"line":859},1,[857,861,863],{"class":862},"sTJeM","async",[857,865,866],{"class":862}," def",[857,868,870],{"class":869},"s3dhs"," me",[857,872,874],{"class":873},"sigWx","(user: CurrentUser) -> dict[",[857,876,878],{"class":877},"sacAq","str",[857,880,881],{"class":873},", Any]: ",[857,883,884],{"class":877},"...\n",[590,886,887,888,890],{},"That reads as ordinary typed Python, autocompletes as ",[603,889,842],{},", and carries its wiring invisibly.",[821,892,894,895,897],{"id":893},"how-annotated-carries-the-wiring","How ",[603,896,625],{}," Carries the Wiring",[590,899,900,903,904,908,909,912,913,915,916,915,919,915,922,915,925,915,928,915,931,934,935,938,939,942],{},[603,901,902],{},"Annotated[X, m1, m2]"," is a type that ",[905,906,907],"em",{},"is"," ",[603,910,911],{},"X"," for typing purposes and additionally carries arbitrary metadata objects. Python itself ignores the metadata; consumers pick out what they recognise. FastAPI walks each parameter's annotation looking for ",[603,914,838],{},", ",[603,917,918],{},"Query",[603,920,921],{},"Path",[603,923,924],{},"Header",[603,926,927],{},"Cookie",[603,929,930],{},"Form",[603,932,933],{},"File"," and ",[603,936,937],{},"Security"," instances; Pydantic looks for ",[603,940,941],{},"Field"," instances and validator markers. They coexist without knowing about each other.",[590,944,945,946,948],{},"Because ",[603,947,625],{}," is just a type, it obeys every rule types obey: it can be assigned to a name, imported, parameterised in a generic, and reused. That is the entire trick.",[848,950,952],{"className":850,"code":951,"language":852,"meta":853,"style":853},"class Session:\n    \"\"\"Stand-in for a database session so the example runs with no external service.\"\"\"\n\n    def __init__(self, name: str = \"primary\") -> None:\n        self.name = name\n\n    def load_user(self, token: str) -> User:\n        return User(id=1, email=f\"{token}@example.com\", role=\"member\")\n\n\nasync def get_session() -> Session:\n    return Session()\n\n\nDbSession = Annotated[Session, Depends(get_session)]\n\n\nasync def get_current_user(\n    session: DbSession,\n    authorization: Annotated[str | None, Header()] = None,\n) -> User:\n    if not authorization or not authorization.startswith(\"Bearer \"):\n        raise HTTPException(401, \"missing bearer token\")\n    return session.load_user(authorization.removeprefix(\"Bearer \"))\n\n\n# One alias, used as a plain type hint everywhere. The dependency travels with the type.\nCurrentUser = Annotated[User, Depends(get_current_user)]\n",[603,953,954,966,973,980,1008,1023,1028,1044,1099,1104,1109,1122,1131,1136,1141,1152,1157,1162,1175,1181,1205,1210,1236,1255,1268,1273,1278,1285],{"__ignoreMap":853},[857,955,956,959,963],{"class":769,"line":859},[857,957,958],{"class":862},"class",[857,960,962],{"class":961},"sV4o_"," Session",[857,964,965],{"class":873},":\n",[857,967,969],{"class":769,"line":968},2,[857,970,972],{"class":971},"sYEJz","    \"\"\"Stand-in for a database session so the example runs with no external service.\"\"\"\n",[857,974,976],{"class":769,"line":975},3,[857,977,979],{"emptyLinePlaceholder":978},true,"\n",[857,981,983,986,989,992,994,997,1000,1003,1006],{"class":769,"line":982},4,[857,984,985],{"class":862},"    def",[857,987,988],{"class":877}," __init__",[857,990,991],{"class":873},"(self, name: ",[857,993,878],{"class":877},[857,995,996],{"class":862}," =",[857,998,999],{"class":971}," \"primary\"",[857,1001,1002],{"class":873},") -> ",[857,1004,1005],{"class":877},"None",[857,1007,965],{"class":873},[857,1009,1011,1014,1017,1020],{"class":769,"line":1010},5,[857,1012,1013],{"class":877},"        self",[857,1015,1016],{"class":873},".name ",[857,1018,1019],{"class":862},"=",[857,1021,1022],{"class":873}," name\n",[857,1024,1026],{"class":769,"line":1025},6,[857,1027,979],{"emptyLinePlaceholder":978},[857,1029,1031,1033,1036,1039,1041],{"class":769,"line":1030},7,[857,1032,985],{"class":862},[857,1034,1035],{"class":869}," load_user",[857,1037,1038],{"class":873},"(self, token: ",[857,1040,878],{"class":877},[857,1042,1043],{"class":873},") -> User:\n",[857,1045,1047,1050,1053,1056,1058,1061,1063,1066,1068,1071,1074,1077,1080,1083,1086,1088,1091,1093,1096],{"class":769,"line":1046},8,[857,1048,1049],{"class":862},"        return",[857,1051,1052],{"class":873}," User(",[857,1054,1055],{"class":961},"id",[857,1057,1019],{"class":862},[857,1059,1060],{"class":877},"1",[857,1062,915],{"class":873},[857,1064,1065],{"class":961},"email",[857,1067,1019],{"class":862},[857,1069,1070],{"class":862},"f",[857,1072,1073],{"class":971},"\"",[857,1075,1076],{"class":862},"{",[857,1078,1079],{"class":873},"token",[857,1081,1082],{"class":862},"}",[857,1084,1085],{"class":971},"@example.com\"",[857,1087,915],{"class":873},[857,1089,1090],{"class":961},"role",[857,1092,1019],{"class":862},[857,1094,1095],{"class":971},"\"member\"",[857,1097,1098],{"class":873},")\n",[857,1100,1102],{"class":769,"line":1101},9,[857,1103,979],{"emptyLinePlaceholder":978},[857,1105,1107],{"class":769,"line":1106},10,[857,1108,979],{"emptyLinePlaceholder":978},[857,1110,1112,1114,1116,1119],{"class":769,"line":1111},11,[857,1113,863],{"class":862},[857,1115,866],{"class":862},[857,1117,1118],{"class":869}," get_session",[857,1120,1121],{"class":873},"() -> Session:\n",[857,1123,1125,1128],{"class":769,"line":1124},12,[857,1126,1127],{"class":862},"    return",[857,1129,1130],{"class":873}," Session()\n",[857,1132,1134],{"class":769,"line":1133},13,[857,1135,979],{"emptyLinePlaceholder":978},[857,1137,1139],{"class":769,"line":1138},14,[857,1140,979],{"emptyLinePlaceholder":978},[857,1142,1144,1147,1149],{"class":769,"line":1143},15,[857,1145,1146],{"class":873},"DbSession ",[857,1148,1019],{"class":862},[857,1150,1151],{"class":873}," Annotated[Session, Depends(get_session)]\n",[857,1153,1155],{"class":769,"line":1154},16,[857,1156,979],{"emptyLinePlaceholder":978},[857,1158,1160],{"class":769,"line":1159},17,[857,1161,979],{"emptyLinePlaceholder":978},[857,1163,1165,1167,1169,1172],{"class":769,"line":1164},18,[857,1166,863],{"class":862},[857,1168,866],{"class":862},[857,1170,1171],{"class":869}," get_current_user",[857,1173,1174],{"class":873},"(\n",[857,1176,1178],{"class":769,"line":1177},19,[857,1179,1180],{"class":873},"    session: DbSession,\n",[857,1182,1184,1187,1189,1192,1195,1198,1200,1202],{"class":769,"line":1183},20,[857,1185,1186],{"class":873},"    authorization: Annotated[",[857,1188,878],{"class":877},[857,1190,1191],{"class":862}," |",[857,1193,1194],{"class":877}," None",[857,1196,1197],{"class":873},", Header()] ",[857,1199,1019],{"class":862},[857,1201,1194],{"class":877},[857,1203,1204],{"class":873},",\n",[857,1206,1208],{"class":769,"line":1207},21,[857,1209,1043],{"class":873},[857,1211,1213,1216,1219,1222,1225,1227,1230,1233],{"class":769,"line":1212},22,[857,1214,1215],{"class":862},"    if",[857,1217,1218],{"class":862}," not",[857,1220,1221],{"class":873}," authorization ",[857,1223,1224],{"class":862},"or",[857,1226,1218],{"class":862},[857,1228,1229],{"class":873}," authorization.startswith(",[857,1231,1232],{"class":971},"\"Bearer \"",[857,1234,1235],{"class":873},"):\n",[857,1237,1239,1242,1245,1248,1250,1253],{"class":769,"line":1238},23,[857,1240,1241],{"class":862},"        raise",[857,1243,1244],{"class":873}," HTTPException(",[857,1246,1247],{"class":877},"401",[857,1249,915],{"class":873},[857,1251,1252],{"class":971},"\"missing bearer token\"",[857,1254,1098],{"class":873},[857,1256,1258,1260,1263,1265],{"class":769,"line":1257},24,[857,1259,1127],{"class":862},[857,1261,1262],{"class":873}," session.load_user(authorization.removeprefix(",[857,1264,1232],{"class":971},[857,1266,1267],{"class":873},"))\n",[857,1269,1271],{"class":769,"line":1270},25,[857,1272,979],{"emptyLinePlaceholder":978},[857,1274,1276],{"class":769,"line":1275},26,[857,1277,979],{"emptyLinePlaceholder":978},[857,1279,1281],{"class":769,"line":1280},27,[857,1282,1284],{"class":1283},"sFeEa","# One alias, used as a plain type hint everywhere. The dependency travels with the type.\n",[857,1286,1288,1291,1293],{"class":769,"line":1287},28,[857,1289,1290],{"class":873},"CurrentUser ",[857,1292,1019],{"class":862},[857,1294,1295],{"class":873}," Annotated[User, Depends(get_current_user)]\n",[590,1297,1298,1299,1301,1302,1305,1306,934,1310,843],{},"Note that ",[603,1300,744],{}," itself consumes ",[603,1303,1304],{},"DbSession",". Aliases compose all the way down, and each layer reads as a normal annotation. The dependency-graph rules that govern caching and cleanup are unchanged by aliasing — they are covered in ",[634,1307,1309],{"href":1308},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002F","dependency caching and use_cache",[634,1311,1313],{"href":1312},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002F","yield dependencies and cleanup order",[1315,1316,1318],"h3",{"id":1317},"layering-a-second-alias-on-the-first","Layering a second alias on the first",[848,1320,1322],{"className":850,"code":1321,"language":852,"meta":853,"style":853},"async def require_admin(user: CurrentUser) -> User:\n    if user.role != \"admin\":\n        raise HTTPException(403, f\"role {user.role!r} may not do this\")\n    return user\n\n\nAdminUser = Annotated[User, Depends(require_admin)]\n\n\n@app.get(\"\u002Fme\")\nasync def me(user: CurrentUser) -> dict[str, Any]:\n    return user.model_dump()\n\n\n@app.get(\"\u002Fadmin\u002Faudit\")\nasync def audit(user: AdminUser) -> dict[str, Any]:\n    return {\"audited_by\": user.email}\n",[603,1323,1324,1336,1351,1380,1387,1391,1395,1405,1409,1413,1426,1441,1448,1452,1456,1467,1483],{"__ignoreMap":853},[857,1325,1326,1328,1330,1333],{"class":769,"line":859},[857,1327,863],{"class":862},[857,1329,866],{"class":862},[857,1331,1332],{"class":869}," require_admin",[857,1334,1335],{"class":873},"(user: CurrentUser) -> User:\n",[857,1337,1338,1340,1343,1346,1349],{"class":769,"line":968},[857,1339,1215],{"class":862},[857,1341,1342],{"class":873}," user.role ",[857,1344,1345],{"class":862},"!=",[857,1347,1348],{"class":971}," \"admin\"",[857,1350,965],{"class":873},[857,1352,1353,1355,1357,1360,1362,1364,1367,1369,1372,1375,1378],{"class":769,"line":975},[857,1354,1241],{"class":862},[857,1356,1244],{"class":873},[857,1358,1359],{"class":877},"403",[857,1361,915],{"class":873},[857,1363,1070],{"class":862},[857,1365,1366],{"class":971},"\"role ",[857,1368,1076],{"class":862},[857,1370,1371],{"class":873},"user.role",[857,1373,1374],{"class":862},"!r}",[857,1376,1377],{"class":971}," may not do this\"",[857,1379,1098],{"class":873},[857,1381,1382,1384],{"class":769,"line":982},[857,1383,1127],{"class":862},[857,1385,1386],{"class":873}," user\n",[857,1388,1389],{"class":769,"line":1010},[857,1390,979],{"emptyLinePlaceholder":978},[857,1392,1393],{"class":769,"line":1025},[857,1394,979],{"emptyLinePlaceholder":978},[857,1396,1397,1400,1402],{"class":769,"line":1030},[857,1398,1399],{"class":873},"AdminUser ",[857,1401,1019],{"class":862},[857,1403,1404],{"class":873}," Annotated[User, Depends(require_admin)]\n",[857,1406,1407],{"class":769,"line":1046},[857,1408,979],{"emptyLinePlaceholder":978},[857,1410,1411],{"class":769,"line":1101},[857,1412,979],{"emptyLinePlaceholder":978},[857,1414,1415,1418,1421,1424],{"class":769,"line":1106},[857,1416,1417],{"class":869},"@app.get",[857,1419,1420],{"class":873},"(",[857,1422,1423],{"class":971},"\"\u002Fme\"",[857,1425,1098],{"class":873},[857,1427,1428,1430,1432,1434,1436,1438],{"class":769,"line":1111},[857,1429,863],{"class":862},[857,1431,866],{"class":862},[857,1433,870],{"class":869},[857,1435,874],{"class":873},[857,1437,878],{"class":877},[857,1439,1440],{"class":873},", Any]:\n",[857,1442,1443,1445],{"class":769,"line":1124},[857,1444,1127],{"class":862},[857,1446,1447],{"class":873}," user.model_dump()\n",[857,1449,1450],{"class":769,"line":1133},[857,1451,979],{"emptyLinePlaceholder":978},[857,1453,1454],{"class":769,"line":1138},[857,1455,979],{"emptyLinePlaceholder":978},[857,1457,1458,1460,1462,1465],{"class":769,"line":1143},[857,1459,1417],{"class":869},[857,1461,1420],{"class":873},[857,1463,1464],{"class":971},"\"\u002Fadmin\u002Faudit\"",[857,1466,1098],{"class":873},[857,1468,1469,1471,1473,1476,1479,1481],{"class":769,"line":1154},[857,1470,863],{"class":862},[857,1472,866],{"class":862},[857,1474,1475],{"class":869}," audit",[857,1477,1478],{"class":873},"(user: AdminUser) -> dict[",[857,1480,878],{"class":877},[857,1482,1440],{"class":873},[857,1484,1485,1487,1490,1493],{"class":769,"line":1159},[857,1486,1127],{"class":862},[857,1488,1489],{"class":873}," {",[857,1491,1492],{"class":971},"\"audited_by\"",[857,1494,1495],{"class":873},": user.email}\n",[590,1497,1498],{},"Real output — unauthenticated, authenticated, and authorised-but-insufficient:",[848,1500,1504],{"className":1501,"code":1503,"language":698,"meta":853},[1502],"language-text","  {\n    \"request\": \"GET \u002Fme  (no Authorization header)\",\n    \"status\": 401,\n    \"response\": {\n      \"detail\": \"missing bearer token\"\n    }\n  },\n  {\n    \"request\": \"GET \u002Fme  (Bearer ada)\",\n    \"status\": 200,\n    \"response\": {\n      \"id\": 1,\n      \"email\": \"ada@example.com\",\n      \"role\": \"member\"\n    }\n  },\n  {\n    \"request\": \"GET \u002Fadmin\u002Faudit  (member role)\",\n    \"status\": 403,\n    \"response\": {\n      \"detail\": \"role 'member' may not do this\"\n    }\n  },\n",[603,1505,1503],{"__ignoreMap":853},[590,1507,1508,1510],{},[603,1509,727],{}," is one word on the route, and behind it sit an authorisation check, an authentication check, a header parse and a session. Adding a fourth layer — a tenancy check, say — means writing one function and one alias, and no route changes at all.",[821,1512,1514],{"id":1513},"reusable-constrained-types","Reusable Constrained Types",[590,1516,1517],{},"The same technique applies to parameters that have nothing to do with dependencies:",[848,1519,1521],{"className":850,"code":1520,"language":852,"meta":853,"style":853},"class PageParams(BaseModel):\n    page: int = Field(default=1, ge=1)\n    per_page: int = Field(default=20, ge=1, le=100)\n\n\n# A class used directly as a dependency; Depends() infers the callable from the annotation.\nPagination = Annotated[PageParams, Depends()]\n\n# Reusable constrained parameter types, defined once.\nSearchTerm = Annotated[str, Query(min_length=2, max_length=50, description=\"Full-text term.\")]\n\n\n@app.get(\"\u002Forders\")\nasync def orders(user: CurrentUser, page: Pagination, q: SearchTerm = \"all\") -> dict[str, Any]:\n    return {\"user\": user.email, \"page\": page.model_dump(), \"q\": q}\n\n\n@app.get(\"\u002Fpublic\u002Fsearch\")\nasync def public_search(q: SearchTerm, page: Pagination) -> dict[str, Any]:\n    # Same aliases reused on an unauthenticated route: no copy-pasted Query(...) arguments.\n    return {\"q\": q, \"page\": page.model_dump()}\n",[603,1522,1523,1537,1568,1606,1610,1614,1619,1629,1633,1638,1683,1687,1691,1702,1726,1750,1754,1758,1769,1785,1790],{"__ignoreMap":853},[857,1524,1525,1527,1530,1532,1535],{"class":769,"line":859},[857,1526,958],{"class":862},[857,1528,1529],{"class":961}," PageParams",[857,1531,1420],{"class":873},[857,1533,1534],{"class":877},"BaseModel",[857,1536,1235],{"class":873},[857,1538,1539,1542,1545,1547,1550,1553,1555,1557,1559,1562,1564,1566],{"class":769,"line":968},[857,1540,1541],{"class":873},"    page: ",[857,1543,1544],{"class":877},"int",[857,1546,996],{"class":862},[857,1548,1549],{"class":873}," Field(",[857,1551,1552],{"class":961},"default",[857,1554,1019],{"class":862},[857,1556,1060],{"class":877},[857,1558,915],{"class":873},[857,1560,1561],{"class":961},"ge",[857,1563,1019],{"class":862},[857,1565,1060],{"class":877},[857,1567,1098],{"class":873},[857,1569,1570,1573,1575,1577,1579,1581,1583,1586,1588,1590,1592,1594,1596,1599,1601,1604],{"class":769,"line":975},[857,1571,1572],{"class":873},"    per_page: ",[857,1574,1544],{"class":877},[857,1576,996],{"class":862},[857,1578,1549],{"class":873},[857,1580,1552],{"class":961},[857,1582,1019],{"class":862},[857,1584,1585],{"class":877},"20",[857,1587,915],{"class":873},[857,1589,1561],{"class":961},[857,1591,1019],{"class":862},[857,1593,1060],{"class":877},[857,1595,915],{"class":873},[857,1597,1598],{"class":961},"le",[857,1600,1019],{"class":862},[857,1602,1603],{"class":877},"100",[857,1605,1098],{"class":873},[857,1607,1608],{"class":769,"line":982},[857,1609,979],{"emptyLinePlaceholder":978},[857,1611,1612],{"class":769,"line":1010},[857,1613,979],{"emptyLinePlaceholder":978},[857,1615,1616],{"class":769,"line":1025},[857,1617,1618],{"class":1283},"# A class used directly as a dependency; Depends() infers the callable from the annotation.\n",[857,1620,1621,1624,1626],{"class":769,"line":1030},[857,1622,1623],{"class":873},"Pagination ",[857,1625,1019],{"class":862},[857,1627,1628],{"class":873}," Annotated[PageParams, Depends()]\n",[857,1630,1631],{"class":769,"line":1046},[857,1632,979],{"emptyLinePlaceholder":978},[857,1634,1635],{"class":769,"line":1101},[857,1636,1637],{"class":1283},"# Reusable constrained parameter types, defined once.\n",[857,1639,1640,1643,1645,1648,1650,1653,1656,1658,1661,1663,1666,1668,1670,1672,1675,1677,1680],{"class":769,"line":1106},[857,1641,1642],{"class":873},"SearchTerm ",[857,1644,1019],{"class":862},[857,1646,1647],{"class":873}," Annotated[",[857,1649,878],{"class":877},[857,1651,1652],{"class":873},", Query(",[857,1654,1655],{"class":961},"min_length",[857,1657,1019],{"class":862},[857,1659,1660],{"class":877},"2",[857,1662,915],{"class":873},[857,1664,1665],{"class":961},"max_length",[857,1667,1019],{"class":862},[857,1669,772],{"class":877},[857,1671,915],{"class":873},[857,1673,1674],{"class":961},"description",[857,1676,1019],{"class":862},[857,1678,1679],{"class":971},"\"Full-text term.\"",[857,1681,1682],{"class":873},")]\n",[857,1684,1685],{"class":769,"line":1111},[857,1686,979],{"emptyLinePlaceholder":978},[857,1688,1689],{"class":769,"line":1124},[857,1690,979],{"emptyLinePlaceholder":978},[857,1692,1693,1695,1697,1700],{"class":769,"line":1133},[857,1694,1417],{"class":869},[857,1696,1420],{"class":873},[857,1698,1699],{"class":971},"\"\u002Forders\"",[857,1701,1098],{"class":873},[857,1703,1704,1706,1708,1711,1714,1716,1719,1722,1724],{"class":769,"line":1138},[857,1705,863],{"class":862},[857,1707,866],{"class":862},[857,1709,1710],{"class":869}," orders",[857,1712,1713],{"class":873},"(user: CurrentUser, page: Pagination, q: SearchTerm ",[857,1715,1019],{"class":862},[857,1717,1718],{"class":971}," \"all\"",[857,1720,1721],{"class":873},") -> dict[",[857,1723,878],{"class":877},[857,1725,1440],{"class":873},[857,1727,1728,1730,1732,1735,1738,1741,1744,1747],{"class":769,"line":1143},[857,1729,1127],{"class":862},[857,1731,1489],{"class":873},[857,1733,1734],{"class":971},"\"user\"",[857,1736,1737],{"class":873},": user.email, ",[857,1739,1740],{"class":971},"\"page\"",[857,1742,1743],{"class":873},": page.model_dump(), ",[857,1745,1746],{"class":971},"\"q\"",[857,1748,1749],{"class":873},": q}\n",[857,1751,1752],{"class":769,"line":1154},[857,1753,979],{"emptyLinePlaceholder":978},[857,1755,1756],{"class":769,"line":1159},[857,1757,979],{"emptyLinePlaceholder":978},[857,1759,1760,1762,1764,1767],{"class":769,"line":1164},[857,1761,1417],{"class":869},[857,1763,1420],{"class":873},[857,1765,1766],{"class":971},"\"\u002Fpublic\u002Fsearch\"",[857,1768,1098],{"class":873},[857,1770,1771,1773,1775,1778,1781,1783],{"class":769,"line":1177},[857,1772,863],{"class":862},[857,1774,866],{"class":862},[857,1776,1777],{"class":869}," public_search",[857,1779,1780],{"class":873},"(q: SearchTerm, page: Pagination) -> dict[",[857,1782,878],{"class":877},[857,1784,1440],{"class":873},[857,1786,1787],{"class":769,"line":1183},[857,1788,1789],{"class":1283},"    # Same aliases reused on an unauthenticated route: no copy-pasted Query(...) arguments.\n",[857,1791,1792,1794,1796,1798,1801,1803],{"class":769,"line":1207},[857,1793,1127],{"class":862},[857,1795,1489],{"class":873},[857,1797,1746],{"class":971},[857,1799,1800],{"class":873},": q, ",[857,1802,1740],{"class":971},[857,1804,1805],{"class":873},": page.model_dump()}\n",[590,1807,1808],{},"The constraints really are enforced on both routes:",[848,1810,1813],{"className":1811,"code":1812,"language":698,"meta":853},[1502],"  {\n    \"request\": \"GET \u002Forders?page=2&per_page=200\",\n    \"status\": 422,\n    \"response\": {\n      \"detail\": [\n        {\n          \"type\": \"less_than_equal\",\n          \"loc\": [\n            \"query\",\n            \"per_page\"\n          ],\n          \"msg\": \"Input should be less than or equal to 100\",\n          \"input\": \"200\",\n          \"ctx\": {\n            \"le\": 100\n          }\n        }\n      ]\n    }\n  },\n  {\n    \"request\": \"GET \u002Fpublic\u002Fsearch?q=a\",\n    \"status\": 422,\n    \"response\": {\n      \"detail\": [\n        {\n          \"type\": \"string_too_short\",\n          \"loc\": [\n            \"query\",\n            \"q\"\n          ],\n          \"msg\": \"String should have at least 2 characters\",\n          \"input\": \"a\",\n          \"ctx\": {\n            \"min_length\": 2\n          }\n        }\n      ]\n    }\n  },\n",[603,1814,1812],{"__ignoreMap":853},[590,1816,1817,1818,1821,1822,1825,1826,1829,1830,1832,1833,1837],{},"Two things worth pointing out. ",[603,1819,1820],{},"Depends()"," with no argument infers the callable from the annotation — ",[603,1823,1824],{},"PageParams"," itself — so a Pydantic model becomes a group of query parameters with one alias, and raising the page ceiling later is a one-line change that propagates everywhere. And ",[603,1827,1828],{},"SearchTerm"," carries its ",[603,1831,1674],{},", so both endpoints document the parameter identically in OpenAPI without anyone copying prose. That consistency is what makes generated client libraries pleasant rather than merely correct; see ",[634,1834,1836],{"href":1835},"\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002F","customizing OpenAPI schema generation"," for the wider picture.",[821,1839,1841],{"id":1840},"why-this-beats-the-default-value-form","Why This Beats the Default-Value Form",[590,1843,1844],{},"The older syntax puts the marker where the default goes:",[848,1846,1848],{"className":850,"code":1847,"language":852,"meta":853,"style":853},"# Legacy: the default slot is occupied by a Depends object.\nasync def me(user: User = Depends(get_current_user)): ...\n",[603,1849,1850,1855],{"__ignoreMap":853},[857,1851,1852],{"class":769,"line":859},[857,1853,1854],{"class":1283},"# Legacy: the default slot is occupied by a Depends object.\n",[857,1856,1857,1859,1861,1863,1866,1868,1871],{"class":769,"line":968},[857,1858,863],{"class":862},[857,1860,866],{"class":862},[857,1862,870],{"class":869},[857,1864,1865],{"class":873},"(user: User ",[857,1867,1019],{"class":862},[857,1869,1870],{"class":873}," Depends(get_current_user)): ",[857,1872,884],{"class":877},[590,1874,1875],{},"Four concrete problems.",[590,1877,1878,908,1881,1884,1885,1887,1888,1890,1891,1893,1894,1897,1898,1900],{},[593,1879,1880],{},"Direct calls break.",[603,1882,1883],{},"await me()"," binds ",[603,1886,834],{}," to a ",[603,1889,838],{}," instance, so any unit test that calls the handler as a function has to construct a ",[603,1892,842],{}," and pass it explicitly — which works, but only if the parameter has no ",[905,1895,1896],{},"other"," defaults competing for position. With ",[603,1899,625],{},", the signature is honest: parameters with defaults have real defaults, and the function is callable.",[590,1902,1903,908,1906,1909,1910,1912,1913,1915],{},[593,1904,1905],{},"Type checkers are lied to.",[603,1907,1908],{},"user: User = Depends(...)"," claims a ",[603,1911,842],{}," default that is not a ",[603,1914,842],{},". Tools either special-case FastAPI or emit a false positive on every route in your codebase.",[590,1917,1918,1921],{},[593,1919,1920],{},"Parameter ordering gets awkward."," Because every dependency occupies a default slot, a single non-default parameter must come first, and you end up reordering arguments for reasons that have nothing to do with readability.",[590,1923,1924,1927,1928,1930,1931,1933],{},[593,1925,1926],{},"It cannot be reused."," A ",[603,1929,838],{}," in a default slot is welded to that parameter. ",[603,1932,625],{}," is a type, and types have names.",[590,1935,1936,1937,1939],{},"The default-value form is not deprecated and existing code does not need rewriting. But the migration is mechanical and can be done route by route: move the marker into ",[603,1938,625],{},", and put the real default (or nothing) in the default slot.",[821,1941,1943],{"id":1942},"testing-overrides-target-the-callable","Testing: Overrides Target the Callable",[590,1945,1946,1947,1949],{},"The most common worry about aliases is that they hide the dependency from ",[603,1948,618],{},". They do not — the alias holds a reference to the same function object, so the override key is unchanged:",[848,1951,1953],{"className":850,"code":1952,"language":852,"meta":853,"style":853},"app.dependency_overrides[get_current_user] = lambda: User(\n    id=99, email=\"admin@example.com\", role=\"admin\"\n)\n",[603,1954,1955,1968,1996],{"__ignoreMap":853},[857,1956,1957,1960,1962,1965],{"class":769,"line":859},[857,1958,1959],{"class":873},"app.dependency_overrides[get_current_user] ",[857,1961,1019],{"class":862},[857,1963,1964],{"class":862}," lambda",[857,1966,1967],{"class":873},": User(\n",[857,1969,1970,1973,1975,1978,1980,1982,1984,1987,1989,1991,1993],{"class":769,"line":968},[857,1971,1972],{"class":961},"    id",[857,1974,1019],{"class":862},[857,1976,1977],{"class":877},"99",[857,1979,915],{"class":873},[857,1981,1065],{"class":961},[857,1983,1019],{"class":862},[857,1985,1986],{"class":971},"\"admin@example.com\"",[857,1988,915],{"class":873},[857,1990,1090],{"class":961},[857,1992,1019],{"class":862},[857,1994,1995],{"class":971},"\"admin\"\n",[857,1997,1998],{"class":769,"line":975},[857,1999,1098],{"class":873},[590,2001,2002,2003,2005],{},"Real output, with no ",[603,2004,656],{}," header on any of these requests:",[848,2007,2010],{"className":2008,"code":2009,"language":698,"meta":853},[1502],"  {\n    \"request\": \"GET \u002Fme  (no header, get_current_user overridden)\",\n    \"status\": 200,\n    \"response\": {\n      \"id\": 99,\n      \"email\": \"admin@example.com\",\n      \"role\": \"admin\"\n    }\n  },\n  {\n    \"request\": \"GET \u002Fadmin\u002Faudit  (override supplies an admin)\",\n    \"status\": 200,\n    \"response\": {\n      \"audited_by\": \"admin@example.com\"\n    }\n  },\n  {\n    \"request\": \"GET \u002Fme  (after clearing overrides)\",\n    \"status\": 401,\n    \"response\": {\n      \"detail\": \"missing bearer token\"\n    }\n  }\n",[603,2011,2009],{"__ignoreMap":853},[590,2013,2014,2015,2018,2019,2021,2022,2021,2024,2026,2027,2029,2030,2032],{},"The middle entry is the interesting one. ",[603,2016,2017],{},"\u002Fadmin\u002Faudit"," uses ",[603,2020,727],{},", which depends on ",[603,2023,749],{},[603,2025,734],{},". Overriding only ",[603,2028,744],{}," — the innermost function — made the real ",[603,2031,749],{}," run against a fabricated admin and pass. That is exactly the behaviour you want in tests: substitute at the boundary, exercise your actual authorisation logic.",[590,2034,2035,2036,843],{},"The last entry confirms the override was cleared. Overrides are global mutable state on the app, so clear them in a fixture teardown or one leaked override will make an unrelated test suite pass for the wrong reason. The fixture patterns are in ",[634,2037,2039],{"href":2038},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002F","overriding dependencies in tests",[590,2041,2042],{},"For tests that do not need the HTTP layer at all, aliased handlers can simply be called:",[848,2044,2046],{"className":850,"code":2045,"language":852,"meta":853,"style":853},"import pytest\n\n\n@pytest.mark.asyncio\nasync def test_audit_rejects_members():\n    with pytest.raises(HTTPException) as exc:\n        await require_admin(User(id=1, email=\"a@b.com\", role=\"member\"))\n    assert exc.value.status_code == 403\n",[603,2047,2048,2056,2060,2064,2069,2081,2095,2128],{"__ignoreMap":853},[857,2049,2050,2053],{"class":769,"line":859},[857,2051,2052],{"class":862},"import",[857,2054,2055],{"class":873}," pytest\n",[857,2057,2058],{"class":769,"line":968},[857,2059,979],{"emptyLinePlaceholder":978},[857,2061,2062],{"class":769,"line":975},[857,2063,979],{"emptyLinePlaceholder":978},[857,2065,2066],{"class":769,"line":982},[857,2067,2068],{"class":869},"@pytest.mark.asyncio\n",[857,2070,2071,2073,2075,2078],{"class":769,"line":1010},[857,2072,863],{"class":862},[857,2074,866],{"class":862},[857,2076,2077],{"class":869}," test_audit_rejects_members",[857,2079,2080],{"class":873},"():\n",[857,2082,2083,2086,2089,2092],{"class":769,"line":1025},[857,2084,2085],{"class":862},"    with",[857,2087,2088],{"class":873}," pytest.raises(HTTPException) ",[857,2090,2091],{"class":862},"as",[857,2093,2094],{"class":873}," exc:\n",[857,2096,2097,2100,2103,2105,2107,2109,2111,2113,2115,2118,2120,2122,2124,2126],{"class":769,"line":1030},[857,2098,2099],{"class":862},"        await",[857,2101,2102],{"class":873}," require_admin(User(",[857,2104,1055],{"class":961},[857,2106,1019],{"class":862},[857,2108,1060],{"class":877},[857,2110,915],{"class":873},[857,2112,1065],{"class":961},[857,2114,1019],{"class":862},[857,2116,2117],{"class":971},"\"a@b.com\"",[857,2119,915],{"class":873},[857,2121,1090],{"class":961},[857,2123,1019],{"class":862},[857,2125,1095],{"class":971},[857,2127,1267],{"class":873},[857,2129,2130,2133,2136,2139],{"class":769,"line":1046},[857,2131,2132],{"class":862},"    assert",[857,2134,2135],{"class":873}," exc.value.status_code ",[857,2137,2138],{"class":862},"==",[857,2140,2141],{"class":877}," 403\n",[590,2143,2144,2145,2147,2148,2151,2152,843],{},"That test never builds an app, a client or a request. It is possible only because ",[603,2146,749],{}," takes ",[603,2149,2150],{},"user: CurrentUser",", which at runtime is just ",[603,2153,842],{},[821,2155,2157],{"id":2156},"where-to-put-the-aliases","Where to Put the Aliases",[590,2159,2160,2161,2165],{},"Import direction is the thing to protect. Two rules avoid the circular imports described in ",[634,2162,2164],{"href":2163},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002F","the dependency injection circular import fix",":",[590,2167,2168,908,2171,2173,2174,2176,2177,2180,2181,2184],{},[593,2169,2170],{},"Dependency aliases live beside their dependency function.",[603,2172,734],{}," belongs in the module that defines ",[603,2175,744],{}," — usually a ",[603,2178,2179],{},"dependencies.py"," or ",[603,2182,2183],{},"security.py",". Routers import from it; it imports from no router.",[590,2186,2187,908,2190,915,2192,915,2195,2198,2199,2202,2203,2207],{},[593,2188,2189],{},"Pure constrained types live in a shared types module.",[603,2191,1828],{},[603,2193,2194],{},"PositiveInt",[603,2196,2197],{},"Slug"," have no dependencies and no I\u002FO, so a leaf ",[603,2200,2201],{},"types.py"," that everything may import is safe by construction. This is the same module where the reusable validators from ",[634,2204,2206],{"href":2205},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002F","creating reusable custom validators"," belong.",[590,2209,2210,2211,2214,2215,2217,2218,2221],{},"Name aliases for what they ",[905,2212,2213],{},"are",", not for how they are obtained: ",[603,2216,734],{},", not ",[603,2219,2220],{},"UserFromToken",". The point of the alias is that the route does not care where the value came from.",[821,2223,2225],{"id":2224},"trade-offs-and-when-not-to","Trade-offs and When Not To",[590,2227,2228,2231,2232,2234],{},[593,2229,2230],{},"Indirection has a cost."," A reader who has never seen ",[603,2233,734],{}," has to jump to its definition to learn that it authenticates. That is one jump, and IDEs make it a keystroke — but it is real, and it argues for a small number of well-named aliases rather than one per parameter.",[590,2236,2237,2240],{},[593,2238,2239],{},"Do not alias a one-off."," A dependency used by a single route is clearer inline. The alias earns its keep at the third call site.",[590,2242,2243,2246,2247,2250],{},[593,2244,2245],{},"Aliases do not change the dependency graph."," Two aliases wrapping ",[603,2248,2249],{},"Depends(get_session)"," are still the same dependency and are still cached within a request. Equally, aliasing does not merge two distinct callables that happen to do the same thing — caching is keyed by the callable object.",[590,2252,2253,2266,2267,934,2270,2273],{},[593,2254,2255,2257,2258,2261,2262,2265],{},[603,2256,625],{}," requires Python 3.9+ for ",[603,2259,2260],{},"typing.Annotated",", and 3.10+ for the ",[603,2263,2264],{},"X | None"," syntax used throughout."," On older interpreters, ",[603,2268,2269],{},"typing_extensions.Annotated",[603,2271,2272],{},"Optional[X]"," work identically.",[821,2275,2277],{"id":2276},"faq","FAQ",[590,2279,2280,2289,2290,2292,2293,2295],{},[593,2281,2282,2283,2285,2286,2288],{},"Why is ",[603,2284,625],{}," better than putting ",[603,2287,838],{}," in the default value?","\nBecause the default slot then holds a ",[603,2291,838],{}," object rather than a real default, which breaks direct calls in tests and confuses type checkers. With ",[603,2294,625],{}," the metadata lives in the type, so the parameter's declared type is the type you actually receive and the function stays an ordinary callable.",[590,2297,2298,2304,2305,2307,2308,2310],{},[593,2299,2300,2301,2303],{},"Does aliasing a dependency as a type change how ",[603,2302,618],{}," works?","\nNo. The override key is the underlying callable, not the alias. Overriding ",[603,2306,744],{}," replaces it for every route that uses the ",[603,2309,734],{}," alias, because the alias is just a reference to the same function object.",[590,2312,2313,2319,2320,2322,2323,2325,2326,2328],{},[593,2314,2315,2316,2318],{},"Can I stack several pieces of metadata in one ",[603,2317,625],{},"?","\nYes. ",[603,2321,625],{}," accepts any number of metadata objects and FastAPI reads the ones it recognises while Pydantic reads its own. A single alias can carry a ",[603,2324,918],{}," marker, a ",[603,2327,941],{}," constraint and a custom validator at once.",[590,2330,2331,2334],{},[593,2332,2333],{},"Should a reusable annotated type live next to the model or in its own module?","\nPut dependency aliases in the module that owns the dependency function, and pure constrained types in a shared types module. That keeps import direction one-way and avoids the circular imports that come from a module importing routes to get an alias.",[590,2336,2337,2343],{},[593,2338,2339,2340,2342],{},"Does using ",[603,2341,625],{}," aliases cost anything at runtime?","\nNo measurable amount. FastAPI inspects signatures once at import time to build the dependency graph, so an alias reused on forty routes resolves to the same metadata objects and adds no per-request work.",[821,2345,2347],{"id":2346},"related-reading","Related Reading",[597,2349,2350,2358,2366,2377],{},[600,2351,2352,908,2355,2357],{},[593,2353,2354],{},"Up to the topic:",[634,2356,637],{"href":636}," for how precise types pay off across a large model layer.",[600,2359,2360,908,2363,2365],{},[593,2361,2362],{},"The declaration rules:",[634,2364,645],{"href":644}," explains what FastAPI does with the metadata these aliases carry.",[600,2367,2368,908,2371,934,2374,843],{},[593,2369,2370],{},"Dependency design:",[634,2372,443],{"href":2373},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002F",[634,2375,2376],{"href":2038},"Overriding Dependencies in Tests",[600,2378,2379,908,2382,2384],{},[593,2380,2381],{},"Reusable validation:",[634,2383,25],{"href":2205}," for the validator half of the same idea.",[2386,2387,2388],"style",{},"html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}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 .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 .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}",{"title":853,"searchDepth":968,"depth":968,"links":2390},[2391,2392,2396,2397,2398,2399,2400,2401,2402],{"id":823,"depth":968,"text":824},{"id":893,"depth":968,"text":2393,"children":2394},"How Annotated Carries the Wiring",[2395],{"id":1317,"depth":975,"text":1318},{"id":1513,"depth":968,"text":1514},{"id":1840,"depth":968,"text":1841},{"id":1942,"depth":968,"text":1943},{"id":2156,"depth":968,"text":2157},{"id":2224,"depth":968,"text":2225},{"id":2276,"depth":968,"text":2277},{"id":2346,"depth":968,"text":2347},"2026-07-20","Use Annotated to declare FastAPI dependencies and constrained params, build shared aliases such as CurrentUser, and keep handlers callable and easy to override.","md",[2407,2410,2413,2416,2418],{"q":2408,"a":2409},"Why is Annotated better than putting Depends in the default value?","Because the default slot then holds a Depends object rather than a real default, which breaks direct calls in tests and confuses type checkers. With Annotated the metadata lives in the type, so the parameter's declared type is the type you actually receive and the function stays an ordinary callable.",{"q":2411,"a":2412},"Does aliasing a dependency as a type change how dependency_overrides works?","No. The override key is the underlying callable, not the alias. Overriding get_current_user replaces it for every route that uses the CurrentUser alias, because the alias is just a reference to the same function object.",{"q":2414,"a":2415},"Can I stack several pieces of metadata in one Annotated?","Yes. Annotated accepts any number of metadata objects and FastAPI reads the ones it recognises while Pydantic reads its own. A single alias can carry a Query marker, a Field constraint and a custom validator at once.",{"q":2333,"a":2417},"Put dependency aliases in the module that owns the dependency function, and pure constrained types in a shared types module. That keeps import direction one-way and avoids the circular imports that come from a module importing routes to get an alias.",{"q":2419,"a":2420},"Does using Annotated aliases cost anything at runtime?","No measurable amount. FastAPI inspects signatures once at import time to build the dependency graph, so an alias reused on forty routes resolves to the same metadata objects and adds no per-request work.",null,{"slug":2423,"breadcrumb":2424},"annotated-dependencies-and-reusable-types",[2425,2428,2431,2433],{"label":2426,"path":2427},"Home","\u002F",{"label":2429,"path":2430},"Advanced Pydantic Validation & Serialization","\u002Fadvanced-pydantic-validation-serialization\u002F",{"label":2432,"path":636},"Type Hinting & IDE Integration",{"label":2434,"path":2435},"Annotated Dependencies and Reusable Types","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002F",{"title":181,"description":2404},"article","tMOg1i-9MHrsa942hc4C7XgF4rhj3d4BDChG-QolVMc",[2421,2421],1784588202620]