Skip to main content

Dependency Injection

Dependency Injection in FastAPI is a system for managing shared logic, database connections, and security requirements across path operations. It allows you to declare "dependables"—callables like functions or classes—that FastAPI resolves and injects into your path operation functions.

Declaring Dependencies

The primary entry point for the dependency injection system is the Depends function, defined in fastapi/param_functions.py. While you can use it as a default value for a parameter, the modern and preferred approach in FastAPI is using Annotated.

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

When a request hits the /items/ endpoint, FastAPI calls common_parameters, extracts the relevant query parameters, and passes the resulting dictionary to read_items.

Class-Based Dependencies

FastAPI can use classes as dependencies. This is useful for maintaining state or using inheritance to share logic. If a class is used as a dependency, FastAPI will instantiate it (if it's the dependency itself) or call its __call__ method.

As seen in tests/test_dependency_class.py, you can use classes with __call__ to create callable dependency instances:

class CallableDependency:
def __call__(self, value: str) -> str:
return value

callable_dependency = CallableDependency()

@app.get("/callable-dependency")
async def get_callable_dependency(value: str = Depends(callable_dependency)):
return value

You can also use the class itself as the dependency. FastAPI will look at the __init__ signature to resolve its own dependencies:

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
return commons

Hierarchical Resolution and the Dependant Graph

Internally, FastAPI represents every dependency and path operation using the Dependant class (found in fastapi/dependencies/models.py). This class tracks:

  • Parameters (path, query, header, cookie, body)
  • Sub-dependencies (dependencies: list["Dependant"])
  • The callable to execute (call)
  • The execution scope

The solve_dependencies function in fastapi/dependencies/utils.py is the engine that resolves this graph. It recursively traverses the Dependant objects, resolving sub-dependencies before calling the parent dependency.

Caching

By default, FastAPI caches the result of a dependency within a single request. If multiple dependencies (or the path operation itself) require the same dependency, it is only executed once. This behavior is controlled by the use_cache parameter in Depends.

If you need a dependency to re-execute every time it is encountered, set use_cache=False:

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters, use_cache=False)]):
return commons

Lifecycle Management with Yield

Dependencies can use yield instead of return to provide a setup and teardown phase. This is the standard way to manage resources like database sessions.

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

Dependency Scopes

FastAPI introduces a scope parameter in Depends to control when the teardown (the code after yield) occurs. This is particularly important when using StreamingResponse.

  1. scope="function": The dependency starts before the path operation and ends immediately after it finishes, but before the response is sent.
  2. scope="request" (Default for yield): The dependency starts before the path operation and ends after the response has been sent to the client.

As demonstrated in tests/test_dependency_yield_scope.py, using scope="request" is necessary if the response (like a stream) needs the resource to remain open:

def dep_session():
s = Session()
yield s
s.open = False

@app.get("/request-scope")
def request_scope(session: Annotated[Session, Depends(dep_session, scope="request")]) -> Any:
def iter_data():
# session.open is still True here because the response hasn't finished
yield json.dumps({"is_open": session.open})
return StreamingResponse(iter_data())

Warning: A dependency with scope="request" cannot depend on a dependency with scope="function". Doing so will raise a FastAPIError during application startup because the "request" scoped dependency would outlive its "function" scoped sub-dependency.

Security Dependencies

The Security function (in fastapi/param_functions.py) is a specialized version of Depends. It functions identically to Depends but allows you to define OAuth2 scopes.

from fastapi import Security

@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Security(get_current_active_user, scopes=["items"])]
):
return [{"item_id": "Foo", "owner": current_user.username}]

These scopes are integrated into the Dependant model and are used to generate the OpenAPI security requirements, making them visible in the automatic documentation (Swagger UI).

Global and Router Dependencies

Dependencies can be applied to an entire FastAPI application or an APIRouter. These dependencies are executed for every request to the corresponding routes, but their return values are not passed to the path operation functions.

# Global dependency
app = FastAPI(dependencies=[Depends(verify_token)])

# Router-level dependency
router = APIRouter(dependencies=[Depends(verify_key)])

These are often used for authentication, logging, or ensuring certain headers are present across a group of endpoints.