Dependency Chains and Overrides

Dependency Chains and Overrides

Master the advanced side of DI. Learn how to nest dependencies and how to override them for seamless automated testing.

Dependency Chains and Overrides

FastAPI's Dependency Injection system isn't just flat; it's a Graph. Dependencies can themselves depend on other dependencies. This allows you to build complex, layered logic while keeping each piece of code tiny and focused.

In this lesson, we explore nested dependencies and the power of testing overrides.


1. Nested Dependencies (Chains)

Imagine you want to check if a user is an Admin.

  1. The check_admin dependency depends on...
  2. The get_current_user dependency, which depends on...
  3. The get_token dependency.

FastAPI will resolve this entire tree automatically.

async def get_current_user(token: str = Depends(get_token)):
    user = db.find_user(token)
    return user

async def check_admin(user: User = Depends(get_current_user)):
    if not user.is_admin:
        raise HTTPException(status_code=403, detail="Admins only")
    return user

@app.get("/admin-panel")
def read_admin(admin: User = Depends(check_admin)):
    return {"message": f"Welcome, Admin {admin.username}"}

2. Dependency Overrides (The Testing Secret)

This is arguably the most powerful feature for production apps. When you are running automated tests, you don't want to connect to your real database or send real emails.

FastAPI allows you to Override any dependency in your app.dependency_overrides dictionary.

# In your test file:
def get_fake_db():
    return MockDatabase()

# Tell the app to use the fake DB instead of the real one!
app.dependency_overrides[get_db] = get_fake_db

# Run your tests...
# After tests, clear the overrides
app.dependency_overrides = {}

3. Sub-dependencies with Parameters

You can create dependencies that are actually Classes. This allows you to pass parameters to the dependency itself.

class SearchSettings:
    def __init__(self, q: str | None = None, limit: int = 10):
        self.q = q
        self.limit = limit

@app.get("/items/")
async def read_items(search: SearchSettings = Depends(SearchSettings)):
    return {"query": search.q, "limit": search.limit}

4. Cache and Singleton Patterns

FastAPI correctly handles dependencies that are used multiple times in the same request. If Dependency A is required by both Dependency B and your Endpoint, FastAPI will only run Dependency A once and share the result (cache).


Visualizing the Graph

graph TD
    A["Request"] --> B["Admin Check"]
    B --> C["User Check"]
    C --> D["Token Extraction"]
    D --> E["Header Parsing"]
    
    E & D & C & B --> F["Final Endpoint Function"]

Summary

  • Deep Chains: FastAPI handles the "Nesting" so you don't have to.
  • Overrides: The foundation of robust, isolated automated testing.
  • Class Dependencies: Use __init__ to create reusable logic with parameters.
  • Efficiency: Results are cached within a single request context.

In the next lesson, we wrap up Module 6 with Exercises to test your dependency injection logic.


Exercise: The Chain Architect

Design a chain for a Subscription Service:

  1. get_user: Fetches a user from a database.
  2. get_subscription: Depends on get_user and checks if they have an active plan.
  3. require_pro_plan: Depends on get_subscription and raises an error if the plan is not "Pro".

How would you use require_pro_plan on a VIP endpoint?

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn