Dependency injection is a technique where objects receive their dependencies (objects they depend on) from an external source rather than creating them internally. For example, if a class A
needs an instance of class B
to function, instead of creating an instance of B
inside A
, an instance of B
is passed to A
as an argument.
In FastAPI, dependencies are functions that are called before the route function. They can return values that are then passed as arguments to the route function.
from fastapi import Depends, FastAPI
app = FastAPI()
# Dependency function
def get_query_param(q: str = None):
return q
@app.get("/items/")
async def read_items(q: str = Depends(get_query_param)):
if q:
return {"query": q}
return {"message": "No query parameter provided"}
In this example, the get_query_param
function is a dependency. It extracts the query parameter q
from the request. The read_items
route function uses this dependency by passing it to the Depends
function.
You can also use classes as dependencies. The class should have a __call__
method that returns the value to be used as the dependency.
from fastapi import Depends, FastAPI
app = FastAPI()
class QueryParam:
def __init__(self, q: str = None):
self.q = q
def __call__(self):
return self.q
@app.get("/items/")
async def read_items(q: str = Depends(QueryParam())):
if q:
return {"query": q}
return {"message": "No query parameter provided"}
Dependencies can be nested. A dependency function can itself depend on other dependencies.
from fastapi import Depends, FastAPI
app = FastAPI()
def get_sub_dependency():
return "sub_dependency_value"
def get_main_dependency(sub_dependency: str = Depends(get_sub_dependency)):
return f"Main dependency with sub: {sub_dependency}"
@app.get("/items/")
async def read_items(main_dependency: str = Depends(get_main_dependency)):
return {"dependency_result": main_dependency}
Dependencies can be reused across multiple route functions. This is useful when you have common functionality that needs to be executed before multiple routes.
from fastapi import Depends, FastAPI
app = FastAPI()
def common_dependency():
return {"message": "Common dependency executed"}
@app.get("/items/")
async def read_items(common: dict = Depends(common_dependency)):
return {"items": ["item1", "item2"], "common": common}
@app.get("/users/")
async def read_users(common: dict = Depends(common_dependency)):
return {"users": ["user1", "user2"], "common": common}
One common use case of dependencies is for authentication. You can create a dependency function that checks if the user is authenticated and raises an exception if not.
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def authenticate_user(credentials: HTTPBasicCredentials = Depends(security)):
# Here you would implement real authentication logic
if credentials.username != "admin" or credentials.password != "password":
raise HTTPException(status_code=401, detail="Invalid credentials")
return credentials.username
@app.get("/protected/")
async def protected_route(user: str = Depends(authenticate_user)):
return {"message": f"Hello, {user}! This is a protected route."}
Always use type hints in your dependency functions and route functions. This makes the code more readable and helps FastAPI generate accurate OpenAPI documentation.
Dependencies should have a single responsibility. Avoid creating overly complex dependencies that do too many things. This makes the code harder to understand and maintain.
When writing unit tests for your FastAPI application, use dependency injection to replace real dependencies with mock objects. This allows you to isolate the unit of code you are testing.
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
# Mock dependency function
def mock_get_query_param():
return "test_query"
def test_read_items():
response = client.get("/items/", params={"q": "test_query"}, dependencies=[Depends(mock_get_query_param)])
assert response.status_code == 200
assert response.json() == {"query": "test_query"}
Dependency injection is a powerful feature in FastAPI that can significantly improve the modularity, testability, and maintainability of your code. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can effectively use dependency injection in your FastAPI applications. Whether it’s for extracting query parameters, performing authentication, or reusing common functionality, dependency injection provides a flexible and efficient way to manage dependencies in your web API.