Bird
Raised Fist0
FastAPIframework~15 mins

Shared dependencies across routers in FastAPI - Deep Dive

Choose your learning style10 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.

Practice

(1/5)
1. What is the main benefit of using shared dependencies across routers in FastAPI?
easy
A. To disable dependency injection in routers
B. To make the API slower by adding extra layers
C. To force all routers to use the same path prefix
D. To avoid repeating the same dependency code in multiple routers

Solution

  1. Step 1: Understand what shared dependencies do

    Shared dependencies allow you to write a dependency once and apply it to many routers, avoiding repetition.
  2. Step 2: Identify the benefit in code maintenance

    This reduces duplicate code and makes the project easier to maintain and update.
  3. Final Answer:

    To avoid repeating the same dependency code in multiple routers -> Option D
  4. Quick Check:

    Shared dependencies = avoid repetition [OK]
Hint: Shared dependencies reduce repeated code across routers [OK]
Common Mistakes:
  • Thinking shared dependencies slow down the API
  • Confusing shared dependencies with path prefixes
  • Believing shared dependencies disable injection
2. Which is the correct way to apply a shared dependency to an APIRouter in FastAPI?
easy
A. router = APIRouter(shared_dependencies=[Depends(common_dep)])
B. router = APIRouter(dependencies=[Depends(common_dep)])
C. router = APIRouter(dependency=Depends(common_dep))
D. router = APIRouter(shared=Depends(common_dep))

Solution

  1. Step 1: Recall the APIRouter parameter for dependencies

    The correct parameter name is dependencies, which takes a list of Depends objects.
  2. Step 2: Match the syntax to the correct option

    router = APIRouter(dependencies=[Depends(common_dep)]) uses dependencies=[Depends(common_dep)], which is the correct syntax.
  3. Final Answer:

    router = APIRouter(dependencies=[Depends(common_dep)]) -> Option B
  4. Quick Check:

    dependencies param = correct syntax [OK]
Hint: Use 'dependencies' param with a list of Depends [OK]
Common Mistakes:
  • Using 'shared_dependencies' instead of 'dependencies'
  • Passing Depends directly without a list
  • Using wrong parameter names like 'dependency' or 'shared'
3. Given this code snippet, what will be printed when accessing /items/42?
from fastapi import FastAPI, APIRouter, Depends

app = FastAPI()

def common_dep():
    print("Common dependency called")

router = APIRouter(dependencies=[Depends(common_dep)])

@router.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

app.include_router(router)
medium
A. "Common dependency called" is printed once per request
B. An error occurs because common_dep is not awaited
C. Nothing is printed
D. "Common dependency called" is printed twice per request

Solution

  1. Step 1: Understand how dependencies run in FastAPI

    Dependencies listed in the router's dependencies run for every matching request automatically.
  2. Step 2: Analyze the print statement in common_dep

    Each time the endpoint is called, common_dep runs and prints "Common dependency called" once.
  3. Final Answer:

    "Common dependency called" is printed once per request -> Option A
  4. Quick Check:

    Router dependencies run once per request [OK]
Hint: Router dependencies run once per matching request [OK]
Common Mistakes:
  • Thinking dependencies run multiple times per request
  • Expecting no output from print statements
  • Confusing async with sync dependencies causing errors
4. Identify the error in this FastAPI router setup with shared dependencies:
from fastapi import APIRouter, Depends

def verify_token():
    pass

router = APIRouter(dependencies=Depends(verify_token))

@router.get("/secure")
async def secure_endpoint():
    return {"message": "Secure"}
medium
A. Depends should be inside a list in dependencies parameter
B. verify_token must return a value
C. APIRouter cannot have dependencies
D. The route path must start with a slash

Solution

  1. Step 1: Check the dependencies parameter type

    The dependencies parameter expects a list of Depends objects, not a single Depends instance.
  2. Step 2: Identify the correct syntax

    It should be dependencies=[Depends(verify_token)] to be valid.
  3. Final Answer:

    Depends should be inside a list in dependencies parameter -> Option A
  4. Quick Check:

    dependencies param requires a list [OK]
Hint: Always wrap Depends in a list for dependencies param [OK]
Common Mistakes:
  • Passing Depends directly without list
  • Assuming dependencies param accepts single Depends
  • Ignoring syntax errors from wrong dependencies format
5. You want to apply a shared dependency that extracts a user from a token to two routers, router_a and router_b. Which approach correctly shares the dependency without repeating code?
hard
A. Define the dependency in each route function separately in both routers
B. Include get_current_user as a parameter in FastAPI() app initialization
C. Add the dependency to each router using the dependencies parameter with Depends(get_current_user)
D. Use global variables to store the user and access it in routes

Solution

  1. Step 1: Understand how to share dependencies across routers

    Using the dependencies parameter in APIRouter allows applying a dependency to all routes in that router.
  2. Step 2: Apply the dependency to both routers

    Adding dependencies=[Depends(get_current_user)] to both router_a and router_b shares the logic without repeating it in each route.
  3. Final Answer:

    Add the dependency to each router using the dependencies parameter with Depends(get_current_user) -> Option C
  4. Quick Check:

    Shared dependencies via router dependencies param [OK]
Hint: Use dependencies param on routers to share common logic [OK]
Common Mistakes:
  • Repeating dependency code in every route
  • Trying to add dependencies in FastAPI app instead of routers
  • Using global variables instead of dependency injection
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard