Dependency injection is one of FastAPI’s most powerful features, enabling clean, modular, and testable code. But beyond simple function-based dependencies, FastAPI offers several advanced patterns that can make your applications even more flexible. In this article, we’ll explore some lesser-known techniques you can use to level up your FastAPI dependency management.
Recap: What is Dependency Injection in FastAPI?
In FastAPI, dependencies are functions (or classes) that perform reusable operations—like authentication, database connection management, or parameter validation. You can inject dependencies into your path operation functions using the Depends
marker:
from fastapi import Depends, FastAPI
def get_token_header(x_token: str = Header(...)):
if x_token != "expected-token":
raise HTTPException(status_code=400, detail="Invalid X-Token header")
app = FastAPI()
@app.get("/protected")
async def protected_route(token: str = Depends(get_token_header)):
return {"message": "Token validated"}
But there’s so much more you can do!
Advanced Technique 1: Dependency Classes for State and Caching
You can turn a dependency into a class by adding a __call__
method. This lets you maintain internal state or cache data between multiple calls within the same request. For example, you might want to lazy-load a database session:
class DBSession:
def __init__(self):
self._session = None
def __call__(self):
if self._session is None:
self._session = create_db_session()
return self._session
db_session = DBSession()
@app.get("/users/{user_id}")
def get_user(user_id: int, db=Depends(db_session)):
user = db.query(User).get(user_id)
return user
For more on this, check out my previous article: [Custom Dependency Classes in FastAPI: Cleaner and More Reusable Code].
Advanced Technique 2: Dependency Overrides for Testing
When unit-testing your FastAPI endpoints, dependency overrides can isolate your application from the real world. You can inject fakes or mocks by using app.dependency_overrides
during your tests:
def fake_get_current_user():
return User(name="test", id=123)
app.dependency_overrides[get_current_user] = fake_get_current_user
This helps keep your tests deterministic and fast, and is essential for proper CI/CD pipelines.
Advanced Technique 3: Scoped Dependencies with Depends
in Sub-Dependencies
Dependencies can depend on other dependencies! This makes it possible to build complex, layered dependency graphs. For instance, you could create a “requires authentication” dependency that’s itself composed of other checks:
def get_db():
db = create_db_session()
try:
yield db
finally:
db.close()
def get_current_user(db=Depends(get_db)):
# Fetches and returns the current user using the DB.
...
This lets you re-use common logic without repeating yourself, and makes your dependencies more composable.
Conclusion
FastAPI’s dependency injection can do much more than the basics. Once you’re comfortable with the simple patterns, explore using classes, overrides for testing, and building up dependency graphs for maximum flexibility. Well-structured dependencies lead to code that’s not only easier to test, but also more maintainable and DRY.
Have a favorite dependency pattern? Drop a comment below or tweet at me (@FastEddy) with your tips!
— Fast Eddy
Leave a Reply