0
0
FastAPIframework~15 mins

Shared dependencies across routers in FastAPI - Deep Dive

Choose your learning style9 modes available
Overview - Shared dependencies across routers
What is it?
Shared dependencies across routers in FastAPI allow you to define common logic or resources once and use them in multiple route groups. This means you can write code like authentication, database sessions, or logging once and apply it to many routes easily. It helps keep your code clean and avoids repeating the same setup in every router. Routers are like mini-apps that organize your API endpoints.
Why it matters
Without shared dependencies, you would have to repeat the same setup code in every router, which leads to mistakes and harder maintenance. Imagine having to write your login check or database connection code in every file separately. Shared dependencies save time, reduce bugs, and make your API easier to update and understand. This is especially important in bigger projects with many routes.
Where it fits
Before learning shared dependencies, you should understand basic FastAPI routing and how to create simple dependencies. After this, you can learn about advanced dependency injection, security, and middleware in FastAPI. Shared dependencies are a bridge between simple route functions and more complex app-wide logic.
Mental Model
Core Idea
Shared dependencies let you write common setup code once and automatically apply it to many routes grouped in routers.
Think of it like...
It's like having a shared recipe for a group of chefs in a kitchen; instead of each chef figuring out the recipe, they all follow the same instructions to make consistent dishes.
┌───────────────┐
│ Shared Logic  │
│ (Dependency)  │
└──────┬────────┘
       │
┌──────▼───────┐   ┌───────────────┐
│ Router A     │   │ Router B      │
│ (Endpoints)  │   │ (Endpoints)   │
└──────────────┘   └───────────────┘
       │                 │
       └─────► Applied to both routers
Build-Up - 7 Steps
1
FoundationUnderstanding FastAPI Dependencies
🤔
Concept: Learn what dependencies are in FastAPI and how they help share logic.
In FastAPI, dependencies are functions that run before your endpoint code. They can check things like user login or prepare data. You add them using the Depends() function. For example, a dependency can return a database session that your endpoint uses.
Result
You can reuse code easily and keep endpoints clean.
Understanding dependencies is key because they let you separate setup code from your main logic, making your API easier to read and maintain.
2
FoundationWhat Are Routers in FastAPI?
🤔
Concept: Learn how routers group related endpoints for better organization.
Routers in FastAPI are like mini-apps inside your main app. You can put related endpoints together, like all user-related routes in one router. This helps keep your code organized and modular.
Result
Your API code is easier to manage and scale.
Knowing routers helps you see how shared dependencies can apply to groups of routes, not just single endpoints.
3
IntermediateApplying Dependencies to Single Routers
🤔Before reading on: Do you think dependencies added to a router affect only that router's endpoints or all app endpoints? Commit to your answer.
Concept: Learn how to add dependencies to a router so all its endpoints use them automatically.
You can add dependencies to a router by passing a dependencies list when creating it. For example, APIRouter(dependencies=[Depends(common_dependency)]). This means every endpoint in that router will run the common_dependency before executing.
Result
All endpoints in that router share the same setup logic without repeating code.
Knowing that dependencies can be applied at the router level helps you avoid repeating the same dependency in every endpoint.
4
IntermediateSharing Dependencies Across Multiple Routers
🤔Before reading on: Can you share the same dependency instance across routers or do you need to redefine it for each router? Commit to your answer.
Concept: Learn how to reuse the same dependency function across different routers to keep consistency.
Define your dependency function once, then add it to multiple routers in their dependencies list. This way, the same logic runs for all routers that include it. For example, a get_db() function can be shared to provide database sessions everywhere.
Result
Your app has consistent behavior and less duplicated code.
Sharing dependencies across routers ensures uniform behavior and reduces bugs caused by inconsistent setups.
5
AdvancedDependency Overriding and Hierarchy
🤔Before reading on: If a router and an endpoint both have the same dependency, which one runs? Commit to your answer.
Concept: Understand how FastAPI resolves dependencies when they appear at multiple levels.
FastAPI runs dependencies from the app level, then router level, then endpoint level. If the same dependency is declared multiple times, the closest one to the endpoint overrides the others. This lets you customize behavior for specific routes while keeping shared defaults.
Result
You can fine-tune dependencies without losing shared logic.
Knowing the dependency resolution order prevents unexpected behavior and helps you design flexible APIs.
6
AdvancedPerformance and Lifecycle of Shared Dependencies
🤔Before reading on: Do shared dependencies run once per request or once per app lifetime? Commit to your answer.
Concept: Learn how FastAPI manages the execution and cleanup of shared dependencies.
Dependencies run once per request by default. If you use yield in a dependency, FastAPI treats it as a context manager, running cleanup code after the request. Shared dependencies across routers still run separately for each request, ensuring fresh state and safety.
Result
Your app manages resources efficiently and safely.
Understanding dependency lifecycle helps avoid resource leaks and ensures correct behavior in concurrent requests.
7
ExpertAdvanced Dependency Injection Internals
🤔Before reading on: Do you think FastAPI creates new dependency instances per request or reuses them? Commit to your answer.
Concept: Explore how FastAPI internally handles dependency injection and caching.
FastAPI builds a dependency graph for each request, resolving dependencies in order. It caches results of dependencies so if multiple endpoints or dependencies need the same one, it runs only once per request. This caching works even across routers sharing dependencies, improving performance.
Result
Your app runs dependencies efficiently without redundant work.
Knowing FastAPI's caching mechanism explains why shared dependencies don't slow down your app and how to design dependencies for maximum reuse.
Under the Hood
FastAPI uses Python's type hints and the Depends() function to build a graph of dependencies for each incoming request. When a request arrives, FastAPI walks this graph, calling each dependency function in order. If a dependency is shared across routers, FastAPI calls it once per request and reuses the result wherever needed. If a dependency uses yield, FastAPI treats it as a context manager, running cleanup code after the request finishes.
Why designed this way?
This design allows FastAPI to keep endpoint code clean and focused on business logic while handling setup and teardown automatically. It uses Python's async features and type hints to provide a fast, flexible, and easy-to-use dependency injection system. Alternatives like manual setup or global variables were rejected because they are error-prone and hard to maintain.
┌───────────────┐
│ Incoming      │
│ HTTP Request  │
└──────┬────────┘
       │
┌──────▼─────────────┐
│ Dependency Graph    │
│ Builder            │
└──────┬─────────────┘
       │
┌──────▼─────────────┐
│ Dependency Resolver │
│ (calls dependencies│
│  once per request)  │
└──────┬─────────────┘
       │
┌──────▼─────────────┐
│ Endpoint Handler   │
│ (uses resolved     │
│  dependencies)     │
└────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do shared dependencies run once per app or once per request? Commit to your answer.
Common Belief:Shared dependencies run only once when the app starts and then are reused forever.
Tap to reveal reality
Reality:Shared dependencies run once per request to ensure fresh and safe data for each user.
Why it matters:If you assume dependencies run once globally, you might write code that incorrectly shares state between users, causing bugs and security issues.
Quick: Can you add dependencies to a router and expect them to apply to the whole app? Commit to your answer.
Common Belief:Adding dependencies to a router automatically applies them to all other routers and the main app.
Tap to reveal reality
Reality:Dependencies added to a router only apply to that router's endpoints, not others or the main app.
Why it matters:Misunderstanding this leads to missing security checks or setup in some routes, causing inconsistent behavior.
Quick: If a dependency is declared on both router and endpoint, do both run or only one? Commit to your answer.
Common Belief:Both router-level and endpoint-level dependencies always run together.
Tap to reveal reality
Reality:If the same dependency is declared at both levels, the endpoint-level one overrides the router-level one.
Why it matters:Not knowing this can cause unexpected skipping of important setup or duplicated work.
Quick: Does sharing dependencies across routers mean they share the same instance or separate ones? Commit to your answer.
Common Belief:Sharing dependencies means routers share the exact same instance of the dependency object.
Tap to reveal reality
Reality:Each request gets its own instance of the dependency, even if shared across routers, ensuring isolation.
Why it matters:Assuming shared instances can cause unsafe shared state and bugs in concurrent requests.
Expert Zone
1
Shared dependencies benefit from FastAPI's internal caching per request, so even if multiple endpoints or dependencies use the same shared dependency, it runs only once per request.
2
Dependencies using yield act as context managers, allowing setup and teardown logic, which is crucial for managing resources like database connections safely across routers.
3
Router-level dependencies can be combined with endpoint-level dependencies to create layered and flexible dependency injection, but careful ordering and overriding rules apply.
When NOT to use
Avoid using shared dependencies when the logic is highly specific to a single endpoint or when dependencies require different parameters per route. In such cases, define dependencies directly on endpoints. Also, for global app-wide logic, consider middleware instead of dependencies.
Production Patterns
In production FastAPI apps, shared dependencies are commonly used for database sessions, authentication checks, and logging. Teams define these once and apply them to routers grouping related endpoints, ensuring consistent security and resource management. Overriding dependencies per endpoint allows customization without losing shared behavior.
Connections
Dependency Injection (general software design)
Shared dependencies in FastAPI are a specific implementation of the broader dependency injection pattern.
Understanding FastAPI's shared dependencies deepens your grasp of how dependency injection improves modularity and testability in software.
Middleware in Web Frameworks
Shared dependencies and middleware both handle cross-cutting concerns but at different levels and with different scopes.
Knowing the difference helps you choose when to use shared dependencies (per route or router) versus middleware (global for all requests).
Supply Chain Management
Both involve managing shared resources efficiently and ensuring consistent delivery across multiple endpoints or locations.
Seeing shared dependencies like supply chains helps appreciate the importance of consistent, reusable setups to avoid duplication and errors.
Common Pitfalls
#1Adding a dependency only to one router but expecting it to apply to all routers.
Wrong approach:router1 = APIRouter(dependencies=[Depends(common_dep)]) router2 = APIRouter() app.include_router(router1) app.include_router(router2)
Correct approach:common_dependencies = [Depends(common_dep)] router1 = APIRouter(dependencies=common_dependencies) router2 = APIRouter(dependencies=common_dependencies) app.include_router(router1) app.include_router(router2)
Root cause:Misunderstanding that dependencies are scoped to the router they are added to, not globally.
#2Defining a dependency that holds state globally, causing shared state bugs.
Wrong approach:db_session = create_db_session() def get_db(): return db_session # shared instance across requests
Correct approach:def get_db(): db = create_db_session() try: yield db finally: db.close()
Root cause:Not realizing dependencies run per request and should provide fresh instances to avoid unsafe shared state.
#3Declaring the same dependency on both router and endpoint without understanding override behavior.
Wrong approach:router = APIRouter(dependencies=[Depends(dep1)]) @router.get('/', dependencies=[Depends(dep2)]) async def endpoint(): pass
Correct approach:router = APIRouter(dependencies=[Depends(dep1)]) @router.get('/') async def endpoint(dep=Depends(dep2)): pass
Root cause:Confusing the difference between dependencies parameter and Depends() in function signature, leading to unexpected overrides.
Key Takeaways
Shared dependencies in FastAPI let you write common setup code once and apply it to many routes grouped in routers, improving code reuse and consistency.
Dependencies run once per request, ensuring fresh and safe data for each user, even when shared across routers.
Router-level dependencies apply only to that router's endpoints, so you must add shared dependencies to each router that needs them.
FastAPI resolves dependencies in a hierarchy: app-level, router-level, then endpoint-level, with closer declarations overriding others.
Understanding FastAPI's dependency injection internals, including caching and lifecycle, helps you write efficient and safe APIs.