Bird
Raised Fist0
FastAPIframework~15 mins

Sub-dependencies in FastAPI - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - Sub-dependencies
What is it?
Sub-dependencies in FastAPI are dependencies that depend on other dependencies. They allow you to build small, reusable pieces of logic that can be combined to create more complex dependency chains. This helps organize code and share common functionality across different parts of an application.
Why it matters
Without sub-dependencies, you would have to repeat the same code in multiple places or write large, complex dependencies that are hard to maintain. Sub-dependencies make your code cleaner, easier to test, and more modular. They help avoid duplication and make your app more scalable and flexible.
Where it fits
Before learning sub-dependencies, you should understand basic FastAPI dependencies and how to use the Depends function. After mastering sub-dependencies, you can explore advanced dependency injection patterns, security schemes, and request lifecycle management in FastAPI.
Mental Model
Core Idea
Sub-dependencies are like building blocks where one block depends on another, creating a chain of reusable logic pieces.
Think of it like...
Imagine making a sandwich where you first prepare the bread and then add fillings. Preparing the bread is a sub-step needed before the final sandwich is ready. Similarly, sub-dependencies prepare parts needed by bigger dependencies.
Dependency Chain:

[Sub-dependency A] β†’ [Dependency B] β†’ [Endpoint Handler]

Each arrow means 'depends on'. The endpoint handler needs Dependency B, which in turn needs Sub-dependency A.
Build-Up - 7 Steps
1
FoundationUnderstanding Basic Dependencies
πŸ€”
Concept: Learn what a dependency is in FastAPI and how to use Depends to inject it.
In FastAPI, dependencies are functions that provide some value or logic to your path operation functions. You use Depends to declare a dependency. For example: from fastapi import FastAPI, Depends app = FastAPI() def common_parameters(q: str = None): return {"q": q} @app.get("/items/") async def read_items(commons: dict = Depends(common_parameters)): return commons
Result
The endpoint receives the dictionary returned by common_parameters automatically.
Understanding basic dependencies is essential because sub-dependencies build on this concept by nesting these dependency calls.
2
FoundationHow Depends Works Internally
πŸ€”
Concept: Explore how FastAPI calls dependency functions and passes their results to other functions.
When FastAPI sees Depends in a function parameter, it calls the dependency function first, gets its return value, and passes it to the main function. This happens automatically during request handling.
Result
Dependencies are resolved in order, and their results are injected into the functions that need them.
Knowing this call order helps understand how sub-dependencies chain together and how data flows through them.
3
IntermediateCreating Sub-dependencies
πŸ€”Before reading on: do you think a dependency can depend on another dependency directly? Commit to yes or no.
Concept: Learn how to make a dependency function that itself uses Depends to call another dependency.
You can declare a dependency inside another dependency by using Depends in its parameters. For example: from fastapi import HTTPException def get_token_header(): return "token123" def verify_token(token: str = Depends(get_token_header)): if token != "token123": raise HTTPException(status_code=400, detail="Invalid token") return token @app.get("/secure-data") async def secure_endpoint(token: str = Depends(verify_token)): return {"token": token}
Result
FastAPI calls get_token_header first, then verify_token, then the endpoint, passing the token through the chain.
Understanding that dependencies can call other dependencies lets you build layered logic that is easier to maintain and test.
4
IntermediateBenefits of Sub-dependencies for Code Reuse
πŸ€”Before reading on: do you think sub-dependencies reduce code duplication or increase it? Commit to your answer.
Concept: See how sub-dependencies help avoid repeating common logic by sharing smaller dependency functions.
Imagine multiple endpoints need to verify a token and also check user permissions. You can create separate dependencies for token verification and permission checking, then combine them: def check_permissions(token: str = Depends(verify_token)): # permission logic here return True @app.get("/admin") async def admin_panel(permission: bool = Depends(check_permissions)): return {"access": "granted"}
Result
The token verification logic is reused by both verify_token and check_permissions, avoiding duplication.
Knowing how to split logic into sub-dependencies leads to cleaner, DRY (Don't Repeat Yourself) code.
5
IntermediatePassing Data Through Sub-dependencies
πŸ€”
Concept: Learn how data returned by one dependency is passed to the next in the chain.
Each dependency returns a value that the next dependency or endpoint receives as a parameter. This allows building pipelines of data processing. Example: def get_user_id(): return 42 def get_user_data(user_id: int = Depends(get_user_id)): return {"user_id": user_id, "name": "Alice"} @app.get("/profile") async def profile(user_data: dict = Depends(get_user_data)): return user_data
Result
The endpoint receives the user data dictionary built from the user ID provided by the sub-dependency.
Understanding data flow through sub-dependencies helps design clear and testable logic chains.
6
AdvancedHandling Async and Sync Sub-dependencies
πŸ€”Before reading on: do you think FastAPI can mix async and sync dependencies in the same chain? Commit to yes or no.
Concept: FastAPI supports both async and sync dependencies, even when nested as sub-dependencies.
You can define dependencies as async def or def. FastAPI will handle calling them correctly. Example: async def async_dep(): return "async result" def sync_dep(data: str = Depends(async_dep)): return f"sync got {data}" @app.get("/mix") async def mixed(dep: str = Depends(sync_dep)): return {"result": dep}
Result
FastAPI runs async_dep asynchronously, then sync_dep synchronously, passing results properly.
Knowing FastAPI's flexibility with async and sync dependencies prevents confusion and enables mixing legacy and modern code.
7
ExpertPerformance and Caching in Sub-dependencies
πŸ€”Before reading on: do you think FastAPI calls the same sub-dependency multiple times per request if used repeatedly? Commit to yes or no.
Concept: FastAPI caches dependency results per request to avoid redundant calls, improving performance in sub-dependency chains.
If a sub-dependency is used multiple times in the same request, FastAPI calls it once and reuses the result. This caching applies even in complex nested dependencies. Example: call_count = 0 def count_calls(): global call_count call_count += 1 return call_count @app.get("/test") async def test(dep1: int = Depends(count_calls), dep2: int = Depends(count_calls)): return {"dep1": dep1, "dep2": dep2, "calls": call_count}
Result
The call_count is 1, showing count_calls ran only once despite being used twice.
Understanding FastAPI's caching avoids performance pitfalls and helps design efficient dependency graphs.
Under the Hood
FastAPI uses Python's function signature inspection to detect dependencies declared with Depends. When handling a request, it recursively resolves dependencies by calling each dependency function, passing results to dependent functions. It caches results per request to avoid repeated calls. Async dependencies are awaited properly, while sync dependencies run normally. This creates a directed acyclic graph of calls executed in order.
Why designed this way?
This design allows maximum flexibility and reusability while keeping the API simple. Recursive resolution supports complex dependency trees without manual wiring. Caching improves performance by preventing redundant work. Supporting both async and sync dependencies ensures compatibility with various codebases.
Request β†’ [Resolve Endpoint Dependencies]
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Dependency A  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Sub-dependencyβ”‚
    β”‚     B         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Sub-dependencyβ”‚
    β”‚     C         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           ↓
    Endpoint Handler
Myth Busters - 4 Common Misconceptions
Quick: Do you think FastAPI calls the same sub-dependency multiple times per request if used repeatedly? Commit to yes or no.
Common Belief:FastAPI calls every dependency function every time it is used, even if repeated in the same request.
Tap to reveal reality
Reality:FastAPI caches dependency results per request, so repeated calls to the same dependency return the cached result.
Why it matters:Without caching, performance would degrade due to redundant calls, especially in complex dependency chains.
Quick: Can a sync dependency call an async dependency directly? Commit to yes or no.
Common Belief:Sync dependencies cannot depend on async dependencies because they can't await them.
Tap to reveal reality
Reality:FastAPI handles this by running async dependencies before sync ones, so sync dependencies can depend on async ones safely.
Why it matters:Misunderstanding this limits how you structure dependencies and may cause unnecessary code duplication.
Quick: Do you think sub-dependencies increase code complexity and reduce clarity? Commit to yes or no.
Common Belief:Using sub-dependencies makes code harder to read and debug because of nested calls.
Tap to reveal reality
Reality:When used properly, sub-dependencies improve modularity and clarity by separating concerns into small, testable units.
Why it matters:Avoiding sub-dependencies due to this misconception leads to large, monolithic dependencies that are harder to maintain.
Quick: Do you think sub-dependencies can share state between requests? Commit to yes or no.
Common Belief:Sub-dependencies can store and share data across different requests automatically.
Tap to reveal reality
Reality:Each request gets a fresh dependency resolution; sub-dependencies do not share state between requests unless explicitly designed with external storage.
Why it matters:Assuming shared state can cause bugs and security issues in web applications.
Expert Zone
1
FastAPI's dependency caching is per-request, but you can disable caching by using 'use_cache=False' in Depends if you need fresh calls.
2
Sub-dependencies can raise exceptions that propagate up the chain, allowing centralized error handling in higher-level dependencies.
3
Dependencies can be parameterized dynamically using classes with __call__, enabling flexible sub-dependency configurations.
When NOT to use
Avoid sub-dependencies when the logic is trivial or used only once, as it adds unnecessary complexity. For global shared state or cross-request caching, use external tools like databases or caches instead of dependencies.
Production Patterns
In production, sub-dependencies are used for layered security (authentication, authorization), database session management, and configuration injection. They enable clean separation of concerns and make testing easier by mocking individual dependency layers.
Connections
Function Composition (Programming)
Sub-dependencies build on the idea of composing small functions to create complex behavior.
Understanding function composition helps grasp how sub-dependencies chain logic and pass data step-by-step.
Supply Chain Management (Logistics)
Sub-dependencies resemble supply chain steps where each step depends on the previous one to deliver a final product.
Seeing sub-dependencies as a supply chain clarifies how data and processing flow through layers to produce the endpoint response.
Dependency Injection (Software Engineering)
Sub-dependencies are a form of dependency injection where dependencies themselves have dependencies.
Knowing general dependency injection principles helps understand the flexibility and testability benefits of sub-dependencies.
Common Pitfalls
#1Calling dependency functions manually inside endpoints instead of using Depends.
Wrong approach:def get_token(): return "token" @app.get("/wrong") async def wrong_endpoint(): token = get_token() # manual call return {"token": token}
Correct approach:def get_token(): return "token" @app.get("/correct") async def correct_endpoint(token: str = Depends(get_token)): return {"token": token}
Root cause:Misunderstanding that FastAPI manages dependency calls and injection automatically.
#2Defining dependencies with side effects that run multiple times unexpectedly.
Wrong approach:def increment_counter(): global count count += 1 return count @app.get("/bad") async def bad_endpoint(count: int = Depends(increment_counter)): return {"count": count}
Correct approach:Use caching or external state management to avoid side effects in dependencies: def get_counter(): return count @app.get("/good") async def good_endpoint(count: int = Depends(get_counter)): return {"count": count}
Root cause:Not realizing dependencies may be called multiple times per request or in tests, causing unintended side effects.
#3Mixing sync and async dependencies incorrectly by awaiting sync functions.
Wrong approach:async def async_dep(): return "data" def sync_dep(): data = await async_dep() # invalid await return data
Correct approach:async def async_dep(): return "data" def sync_dep(): # call sync or refactor to async return "data"
Root cause:Confusing async and sync function rules in Python and FastAPI.
Key Takeaways
Sub-dependencies let you build small, reusable logic pieces that depend on each other, making your FastAPI code modular and clean.
FastAPI automatically resolves and caches dependencies per request, so repeated calls to the same sub-dependency are efficient.
You can mix async and sync dependencies in sub-dependency chains, and FastAPI handles calling them correctly.
Proper use of sub-dependencies reduces code duplication and improves testability by separating concerns into layers.
Misunderstanding dependency caching, async rules, or manual calls leads to common bugs and performance issues.

Practice

(1/5)
1. What is the main purpose of sub-dependencies in FastAPI?
easy
A. To handle HTTP requests directly
B. To create database connections only
C. To reuse small parts of code inside other dependencies
D. To replace middleware functions

Solution

  1. Step 1: Understand what sub-dependencies do

    Sub-dependencies allow you to reuse small parts of code inside other dependencies, making your code cleaner.
  2. Step 2: Compare options with this purpose

    Only To reuse small parts of code inside other dependencies correctly describes this purpose; others describe unrelated tasks.
  3. Final Answer:

    To reuse small parts of code inside other dependencies -> Option C
  4. Quick Check:

    Sub-dependencies = Reuse code [OK]
Hint: Sub-dependencies help reuse code inside dependencies [OK]
Common Mistakes:
  • Thinking sub-dependencies handle HTTP requests directly
  • Confusing sub-dependencies with middleware
  • Assuming sub-dependencies only manage database connections
2. Which of the following is the correct way to declare a sub-dependency in FastAPI?
easy
A. def sub_dep(): return 'data' def main_dep(sub: str = Depends(sub_dep)): return sub
B. def sub_dep(): return 'data' def main_dep(sub: str = sub_dep()): return sub
C. def sub_dep(): return 'data' def main_dep(sub: str = Depends(sub_dep())): return sub
D. def sub_dep(): return 'data' def main_dep(sub: str): return sub

Solution

  1. Step 1: Recall FastAPI dependency syntax

    Dependencies must be passed as functions inside Depends(), not called directly.
  2. Step 2: Analyze each option

    def sub_dep(): return 'data' def main_dep(sub: str = Depends(sub_dep)): return sub correctly uses Depends(sub_dep) without calling it. Options B and C call the function, which is incorrect. def sub_dep(): return 'data' def main_dep(sub: str): return sub lacks Depends entirely.
  3. Final Answer:

    def sub_dep(): return 'data'\n\ndef main_dep(sub: str = Depends(sub_dep)): return sub -> Option A
  4. Quick Check:

    Use Depends(function) without parentheses [OK]
Hint: Use Depends with function name, no parentheses [OK]
Common Mistakes:
  • Calling the dependency function inside Depends()
  • Not using Depends at all
  • Passing the function call result instead of the function
3. Given the code below, what will be the output when calling /items/42?
from fastapi import FastAPI, Depends

app = FastAPI()

def sub_dep():
    return "sub-data"

def main_dep(data: str = Depends(sub_dep)):
    return f"main uses {data}"

@app.get("/items/{item_id}")
async def read_item(item_id: int, info: str = Depends(main_dep)):
    return {"item_id": item_id, "info": info}
medium
A. {"item_id": 42, "info": "main_dep"}
B. {"item_id": 42, "info": "main uses sub-data"}
C. {"item_id": 42, "info": "sub-data"}
D. Runtime error due to missing parameters

Solution

  1. Step 1: Understand dependency chaining

    The read_item endpoint depends on main_dep, which depends on sub_dep. The value from sub_dep is passed to main_dep.
  2. Step 2: Trace returned values

    sub_dep() returns "sub-data". main_dep returns "main uses sub-data". So info in read_item is "main uses sub-data".
  3. Final Answer:

    {"item_id": 42, "info": "main uses sub-data"} -> Option B
  4. Quick Check:

    Sub-dependencies chain output correctly [OK]
Hint: Follow dependency chain outputs step-by-step [OK]
Common Mistakes:
  • Ignoring sub-dependency output in main dependency
  • Assuming direct sub_dep output is returned
  • Expecting runtime errors without cause
4. What is the error in the following FastAPI code using sub-dependencies?
from fastapi import FastAPI, Depends

app = FastAPI()

def sub_dep():
    return "data"

def main_dep(data: str = Depends(sub_dep)):
    return data

@app.get("/test")
async def test_endpoint(info: str = Depends(main_dep())):
    return {"info": info}
medium
A. Calling main_dep() inside Depends instead of passing the function
B. Missing return statement in sub_dep
C. Incorrect route path syntax
D. Using async def for endpoint without await

Solution

  1. Step 1: Identify how Depends should be used

    Depends expects a function reference, not a function call. Calling main_dep() executes it immediately, which is wrong.
  2. Step 2: Check the code for this mistake

    The code uses Depends(main_dep()), which calls the function instead of passing it. It should be Depends(main_dep).
  3. Final Answer:

    Calling main_dep() inside Depends instead of passing the function -> Option A
  4. Quick Check:

    Depends needs function, not function call [OK]
Hint: Pass function to Depends, don't call it [OK]
Common Mistakes:
  • Calling dependency functions inside Depends()
  • Confusing async usage with dependency errors
  • Ignoring Depends syntax rules
5. You want to create a FastAPI endpoint that depends on a main dependency which itself depends on two sub-dependencies. How should you structure the dependencies to ensure both sub-dependencies are called and their results used in the main dependency?
hard
A. Call both sub-dependencies inside main dependency without Depends()
B. Pass sub-dependencies as global variables to main dependency
C. Use a single sub-dependency that returns a tuple of both results
D. Define two sub-dependency functions, then in main dependency use Depends() for both as parameters

Solution

  1. Step 1: Understand sub-dependency usage in main dependency

    FastAPI allows multiple dependencies by declaring parameters with Depends(). To use two sub-dependencies, declare both as parameters with Depends().
  2. Step 2: Evaluate options for correctness

    Define two sub-dependency functions, then in main dependency use Depends() for both as parameters correctly describes this pattern. Call both sub-dependencies inside main dependency without Depends() misses Depends(), so sub-dependencies won't be injected. Use a single sub-dependency that returns a tuple of both results is possible but less clear and not standard. Pass sub-dependencies as global variables to main dependency is incorrect as global variables don't work for dependency injection.
  3. Final Answer:

    Define two sub-dependency functions, then in main dependency use Depends() for both as parameters -> Option D
  4. Quick Check:

    Multiple Depends parameters call multiple sub-dependencies [OK]
Hint: Use multiple Depends() parameters in main dependency [OK]
Common Mistakes:
  • Calling sub-dependencies directly without Depends
  • Trying to pass sub-dependencies as globals
  • Combining sub-dependencies into one without clear structure