[{"data":1,"prerenderedAt":932},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002F":310,"surround-\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002F":930},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"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],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"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",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"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",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"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",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"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",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"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",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"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",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"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",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"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",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"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",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"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",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"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",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"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",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"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",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"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",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"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",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"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",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"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",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"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",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"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",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"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",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"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",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"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",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"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",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"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",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"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",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"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",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"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",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"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",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"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",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":263,"body":312,"description":882,"extension":883,"meta":884,"navigation":433,"path":264,"seo":928,"stem":265,"__hash__":929},"content\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex.md",{"type":313,"value":314,"toc":870},"minimark",[315,319,326,349,363,368,371,375,384,388,393,576,648,652,691,695,774,778,816,820,842,846,866],[316,317,263],"h1",{"id":318},"fixing-fastapi-dependency-injection-circular-imports",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,333,336,339,346],"ul",{},[330,331,332],"li",{},"The cycle is at import time: two modules import each other at the top level.",[330,334,335],{},"The cleanest fix is to depend on a protocol and bind the implementation in the factory.",[330,337,338],{},"Function-local imports break a cycle pragmatically when restructuring is overkill.",[330,340,341,345],{},[342,343,344],"code",{},"TYPE_CHECKING"," removes import-time cost for annotation-only imports.",[330,347,348],{},"All four fixes also tend to improve testability.",[320,350,351,352,357,358,362],{},"This is a focused troubleshooting guide for ",[353,354,356],"a",{"href":355},"\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002F","Dependency Injection Strategies",", and it reinforces the wiring approach from ",[353,359,361],{"href":360},"\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002F","Application Factory Patterns",".",[364,365,367],"h2",{"id":366},"the-problem-this-solves","The Problem This Solves",[320,369,370],{},"A growing FastAPI app reaches a point where a router needs a service and the service needs something from the router's module, and the app stops importing. The error is cryptic because the real cause — an import-time cycle — is structural, not local. This guide gives four reliable fixes.",[364,372,374],{"id":373},"prerequisites","Prerequisites",[327,376,377],{},[330,378,379,380,383],{},"A FastAPI app failing at import with ",[342,381,382],{},"ImportError"," or partially-initialized modules.",[364,385,387],{"id":386},"step-by-step-fixes","Step-by-Step Fixes",[389,390,392],"h3",{"id":391},"_1-depend-on-a-protocol-bind-in-the-factory-preferred","1. Depend on a protocol, bind in the factory (preferred)",[394,395,400],"pre",{"className":396,"code":397,"language":398,"meta":399,"style":399},"language-python shiki shiki-themes github-light","# app\u002Forders\u002Fports.py — no imports of concrete modules.\nfrom typing import Protocol\n\n\nclass OrderRepo(Protocol):\n    async def get(self, order_id: int) -> dict | None: ...\n\n\n# app\u002Forders\u002Frouter.py — depends on the interface, not the implementation.\nfrom fastapi import APIRouter, Request\n\nrouter = APIRouter()\n\n\ndef get_repo(request: Request) -> OrderRepo:\n    return request.app.state.order_repo   # Bound in create_app, not imported.\n","python","",[342,401,402,411,428,435,440,459,496,501,506,512,525,530,542,547,552,564],{"__ignoreMap":399},[403,404,407],"span",{"class":405,"line":406},"line",1,[403,408,410],{"class":409},"sAwPA","# app\u002Forders\u002Fports.py — no imports of concrete modules.\n",[403,412,414,418,422,425],{"class":405,"line":413},2,[403,415,417],{"class":416},"sD7c4","from",[403,419,421],{"class":420},"sgsFI"," typing ",[403,423,424],{"class":416},"import",[403,426,427],{"class":420}," Protocol\n",[403,429,431],{"class":405,"line":430},3,[403,432,434],{"emptyLinePlaceholder":433},true,"\n",[403,436,438],{"class":405,"line":437},4,[403,439,434],{"emptyLinePlaceholder":433},[403,441,443,446,450,453,456],{"class":405,"line":442},5,[403,444,445],{"class":416},"class",[403,447,449],{"class":448},"s7eDp"," OrderRepo",[403,451,452],{"class":420},"(",[403,454,455],{"class":448},"Protocol",[403,457,458],{"class":420},"):\n",[403,460,462,465,468,471,474,478,481,484,487,490,493],{"class":405,"line":461},6,[403,463,464],{"class":416},"    async",[403,466,467],{"class":416}," def",[403,469,470],{"class":448}," get",[403,472,473],{"class":420},"(self, order_id: ",[403,475,477],{"class":476},"sYu0t","int",[403,479,480],{"class":420},") -> ",[403,482,483],{"class":476},"dict",[403,485,486],{"class":416}," |",[403,488,489],{"class":476}," None",[403,491,492],{"class":420},": ",[403,494,495],{"class":476},"...\n",[403,497,499],{"class":405,"line":498},7,[403,500,434],{"emptyLinePlaceholder":433},[403,502,504],{"class":405,"line":503},8,[403,505,434],{"emptyLinePlaceholder":433},[403,507,509],{"class":405,"line":508},9,[403,510,511],{"class":409},"# app\u002Forders\u002Frouter.py — depends on the interface, not the implementation.\n",[403,513,515,517,520,522],{"class":405,"line":514},10,[403,516,417],{"class":416},[403,518,519],{"class":420}," fastapi ",[403,521,424],{"class":416},[403,523,524],{"class":420}," APIRouter, Request\n",[403,526,528],{"class":405,"line":527},11,[403,529,434],{"emptyLinePlaceholder":433},[403,531,533,536,539],{"class":405,"line":532},12,[403,534,535],{"class":420},"router ",[403,537,538],{"class":416},"=",[403,540,541],{"class":420}," APIRouter()\n",[403,543,545],{"class":405,"line":544},13,[403,546,434],{"emptyLinePlaceholder":433},[403,548,550],{"class":405,"line":549},14,[403,551,434],{"emptyLinePlaceholder":433},[403,553,555,558,561],{"class":405,"line":554},15,[403,556,557],{"class":416},"def",[403,559,560],{"class":448}," get_repo",[403,562,563],{"class":420},"(request: Request) -> OrderRepo:\n",[403,565,567,570,573],{"class":405,"line":566},16,[403,568,569],{"class":416},"    return",[403,571,572],{"class":420}," request.app.state.order_repo   ",[403,574,575],{"class":409},"# Bound in create_app, not imported.\n",[394,577,579],{"className":396,"code":578,"language":398,"meta":399,"style":399},"# app\u002Fmain.py — the factory binds the concrete class, breaking the cycle.\nfrom app.orders.repo import SqlOrderRepo\n\n\ndef create_app():\n    app = FastAPI()\n    app.state.order_repo = SqlOrderRepo()\n    app.include_router(router)\n    return app\n",[342,580,581,586,598,602,606,616,626,636,641],{"__ignoreMap":399},[403,582,583],{"class":405,"line":406},[403,584,585],{"class":409},"# app\u002Fmain.py — the factory binds the concrete class, breaking the cycle.\n",[403,587,588,590,593,595],{"class":405,"line":413},[403,589,417],{"class":416},[403,591,592],{"class":420}," app.orders.repo ",[403,594,424],{"class":416},[403,596,597],{"class":420}," SqlOrderRepo\n",[403,599,600],{"class":405,"line":430},[403,601,434],{"emptyLinePlaceholder":433},[403,603,604],{"class":405,"line":437},[403,605,434],{"emptyLinePlaceholder":433},[403,607,608,610,613],{"class":405,"line":442},[403,609,557],{"class":416},[403,611,612],{"class":448}," create_app",[403,614,615],{"class":420},"():\n",[403,617,618,621,623],{"class":405,"line":461},[403,619,620],{"class":420},"    app ",[403,622,538],{"class":416},[403,624,625],{"class":420}," FastAPI()\n",[403,627,628,631,633],{"class":405,"line":498},[403,629,630],{"class":420},"    app.state.order_repo ",[403,632,538],{"class":416},[403,634,635],{"class":420}," SqlOrderRepo()\n",[403,637,638],{"class":405,"line":503},[403,639,640],{"class":420},"    app.include_router(router)\n",[403,642,643,645],{"class":405,"line":508},[403,644,569],{"class":416},[403,646,647],{"class":420}," app\n",[389,649,651],{"id":650},"_2-function-local-import","2. Function-local import",[394,653,655],{"className":396,"code":654,"language":398,"meta":399,"style":399},"def get_service():\n    # Imported at call time, so there is no module-load cycle.\n    from app.services.billing import BillingService\n    return BillingService()\n",[342,656,657,666,671,684],{"__ignoreMap":399},[403,658,659,661,664],{"class":405,"line":406},[403,660,557],{"class":416},[403,662,663],{"class":448}," get_service",[403,665,615],{"class":420},[403,667,668],{"class":405,"line":413},[403,669,670],{"class":409},"    # Imported at call time, so there is no module-load cycle.\n",[403,672,673,676,679,681],{"class":405,"line":430},[403,674,675],{"class":416},"    from",[403,677,678],{"class":420}," app.services.billing ",[403,680,424],{"class":416},[403,682,683],{"class":420}," BillingService\n",[403,685,686,688],{"class":405,"line":437},[403,687,569],{"class":416},[403,689,690],{"class":420}," BillingService()\n",[389,692,694],{"id":693},"_3-type-only-imports-under-type_checking","3. Type-only imports under TYPE_CHECKING",[394,696,698],{"className":396,"code":697,"language":398,"meta":399,"style":399},"from typing import TYPE_CHECKING\n\nif TYPE_CHECKING:                         # Not imported at runtime — no cycle.\n    from app.services.billing import BillingService\n\n\ndef charge(service: \"BillingService\") -> None:\n    ...\n",[342,699,700,711,715,729,739,743,747,769],{"__ignoreMap":399},[403,701,702,704,706,708],{"class":405,"line":406},[403,703,417],{"class":416},[403,705,421],{"class":420},[403,707,424],{"class":416},[403,709,710],{"class":476}," TYPE_CHECKING\n",[403,712,713],{"class":405,"line":413},[403,714,434],{"emptyLinePlaceholder":433},[403,716,717,720,723,726],{"class":405,"line":430},[403,718,719],{"class":416},"if",[403,721,722],{"class":476}," TYPE_CHECKING",[403,724,725],{"class":420},":                         ",[403,727,728],{"class":409},"# Not imported at runtime — no cycle.\n",[403,730,731,733,735,737],{"class":405,"line":437},[403,732,675],{"class":416},[403,734,678],{"class":420},[403,736,424],{"class":416},[403,738,683],{"class":420},[403,740,741],{"class":405,"line":442},[403,742,434],{"emptyLinePlaceholder":433},[403,744,745],{"class":405,"line":461},[403,746,434],{"emptyLinePlaceholder":433},[403,748,749,751,754,757,761,763,766],{"class":405,"line":498},[403,750,557],{"class":416},[403,752,753],{"class":448}," charge",[403,755,756],{"class":420},"(service: ",[403,758,760],{"class":759},"sYBdl","\"BillingService\"",[403,762,480],{"class":420},[403,764,765],{"class":476},"None",[403,767,768],{"class":420},":\n",[403,770,771],{"class":405,"line":503},[403,772,773],{"class":476},"    ...\n",[364,775,777],{"id":776},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,779,780,796,810],{},[330,781,782,788,789,792,793,795],{},[323,783,784,785,362],{},"Hidden cycles through ",[342,786,787],{},"__init__.py"," A package ",[342,790,791],{},"__init__"," that imports submodules can create a cycle indirectly; keep ",[342,794,791],{}," thin.",[330,797,798,801,802,805,806,362],{},[323,799,800],{},"Shared models."," If two domains import each other for a shared type, move that type to a neutral ",[342,803,804],{},"core"," module, per ",[353,807,809],{"href":808},"\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002F","Structuring Large FastAPI Projects for Scale",[330,811,812,815],{},[323,813,814],{},"Function-local overuse."," Many local imports signal a structural problem; prefer the protocol fix.",[364,817,819],{"id":818},"verification","Verification",[394,821,825],{"className":822,"code":823,"language":824,"meta":399,"style":399},"language-bash shiki shiki-themes github-light","# A clean import proves the cycle is gone.\npython -c \"import app.main; print('import OK')\"\n","bash",[342,826,827,832],{"__ignoreMap":399},[403,828,829],{"class":405,"line":406},[403,830,831],{"class":409},"# A clean import proves the cycle is gone.\n",[403,833,834,836,839],{"class":405,"line":413},[403,835,398],{"class":448},[403,837,838],{"class":476}," -c",[403,840,841],{"class":759}," \"import app.main; print('import OK')\"\n",[364,843,845],{"id":844},"related-reading","Related Reading",[327,847,848,856],{},[330,849,850,853,854,362],{},[323,851,852],{},"Up to the topic:"," ",[353,855,356],{"href":355},[330,857,858,853,861,863,864,362],{},[323,859,860],{},"Related guides:",[353,862,361],{"href":360}," and ",[353,865,809],{"href":808},[867,868,869],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}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 .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}",{"title":399,"searchDepth":413,"depth":413,"links":871},[872,873,874,879,880,881],{"id":366,"depth":413,"text":367},{"id":373,"depth":413,"text":374},{"id":386,"depth":413,"text":387,"children":875},[876,877,878],{"id":391,"depth":430,"text":392},{"id":650,"depth":430,"text":651},{"id":693,"depth":430,"text":694},{"id":776,"depth":413,"text":777},{"id":818,"depth":413,"text":819},{"id":844,"depth":413,"text":845},"Resolve circular imports in FastAPI dependency injection: why router and service modules deadlock, and four fixes — function-local imports, protocols, factory wiring, and TYPE_CHECKING.","md",{"slug":885,"type":886,"breadcrumb":887,"datePublished":898,"dateModified":899,"howto":900,"faq":918},"fastapi-dependency-injection-circular-import-fix","long_tail",[888,891,894,895],{"label":889,"path":890},"Home","\u002F",{"label":892,"path":893},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":356,"path":355},{"label":896,"path":897},"Dependency Injection Circular Import Fix","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002F","2026-03-03","2026-06-18",{"name":901,"steps":902},"Fix a circular import in FastAPI dependency injection",[903,906,909,912,915],{"name":904,"text":905},"Identify the cycle","Trace which two modules import each other at module load time.",{"name":907,"text":908},"Depend on a protocol","Have the consumer depend on an abstract interface instead of the concrete module.",{"name":910,"text":911},"Wire in the factory","Bind the concrete implementation onto app.state in create_app so modules need not import each other.",{"name":913,"text":914},"Use function-local imports","Where appropriate, import inside the function rather than at module scope.",{"name":916,"text":917},"Guard type-only imports","Use TYPE_CHECKING for imports needed only for annotations.",[919,922,925],{"q":920,"a":921},"Why does my FastAPI app hang or error at import with circular dependencies?","When module A imports module B at the top level and B imports A, Python cannot finish loading either, so you get an ImportError or a partially-initialized module. In FastAPI this commonly happens when a router imports a service that imports the router, often to share a dependency. The cycle is at import time, not request time.",{"q":923,"a":924},"What is the cleanest fix for a DI circular import?","Depend on an abstract interface and bind the concrete implementation in the application factory, storing it on app.state. The consumer then reads the implementation through a dependency at request time, so neither module imports the other at load time and the cycle disappears. It also improves testability.",{"q":926,"a":927},"Is importing inside a function a legitimate fix?","Yes, for breaking a cycle it is a pragmatic and valid technique, because the import runs at call time rather than load time. It is less clean than depending on an interface, but it is a quick, correct fix when restructuring is not warranted. Reserve it for genuine cycles rather than as a default style.",{"title":263,"description":882},"bkk6Zb8q2TLu5WWQdxs3We8-ClIB5GC3I7Mdb-mpoRQ",[931,931],null,1781809863311]