FastAPI Dependency Injection
FastAPI's dependency injection system is one of its defining features. Instead of manually opening database sessions, parsing tokens, or reading pagination parameters in every endpoint, you declare dependencies with Depends(), and FastAPI calls them for each request, passes their results to your function, and cleans up afterwards. Dependencies can depend on other dependencies, run code before and after the request with yield, and be swapped out in tests with a single line.
Because dependencies are ordinary functions with type hints, they also document themselves: query parameters, headers, and security schemes declared inside dependencies appear automatically in the OpenAPI schema and interactive docs.
TL;DR
- Declare a dependency with
Annotated[Type, Depends(func)]in a path operation's parameters. - Dependencies can take request parameters (query, path, header, cookie, body) and other dependencies (sub-dependencies).
- Within one request, each dependency runs once, and results are cached (
use_cache=Falseto disable). yielddependencies run setup before the endpoint and cleanup after, which is ideal for DB sessions, transactions, and locks.- Apply dependencies to routers or the whole app for cross-cutting concerns such as auth, rate limits, and tenancy.
- Replace dependencies in tests with
app.dependency_overrides.
Quick Example
get_session runs once per request, even though both get_current_user and list_orders use it, so the same session is shared.
Core Concepts
Declaring Dependencies
A dependency is any callable: a function, async function, or class. FastAPI inspects its parameters the same way it inspects path operations, so a dependency can declare query parameters, headers, cookies, body fields, or Request, and they're validated and documented automatically. The recommended style uses Annotated, often with reusable type aliases (SessionDep, CurrentUser) to keep endpoint signatures short.
Sub-Dependencies and Caching
Dependencies form a graph: list_orders → get_current_user → get_session. FastAPI resolves it per request, calling each dependency once and reusing the result wherever it's needed. That's why a database session injected into both an auth dependency and an endpoint is the same session. Use Depends(func, use_cache=False) when you need a fresh value each time.
yield Dependencies
Code before yield runs before the endpoint; code after runs once the response is handled:
- Exceptions raised in the endpoint propagate into the dependency, so
try/except/finallyor context managers can roll back and clean up. - In recent FastAPI versions, exit code runs before the response is sent (by default), so errors in cleanup can affect the response. Background tasks shouldn't rely on the dependency's resources still being open.
Class-Based and Parameterized Dependencies
- Classes work as dependencies: FastAPI calls the constructor with resolved parameters (
Depends()with no argument uses the annotated type). - Parameterized dependencies are callable instances configured at declaration time:
Path, Router, and App Dependencies
dependencies=[Depends(...)]on a path operation runs the dependency without passing its value (it's useful for checks).APIRouter(dependencies=[...])applies to every route in a router, for example admin-only routers.FastAPI(dependencies=[...])applies globally.
Overrides for Testing
Any dependency, including the database session, can be replaced with a test version. See FastAPI testing.
Dependencies vs Middleware vs Lifespan
Create expensive shared resources (a database engine, an httpx.AsyncClient) in the lifespan handler, and hand out per-request pieces (sessions) via dependencies.
Best Practices
Use Annotated Type Aliases
Define aliases like SessionDep = Annotated[AsyncSession, Depends(get_session)] once and reuse them. Signatures stay readable and consistent, and the aliases work with editors and type checkers.
Keep Dependencies Small and Composable
One responsibility each (get session, get user, check permission, parse pagination), composed through sub-dependencies. Large "do everything" dependencies are hard to reuse and test.
Manage Transactions Explicitly
Decide where transactions begin and commit: in a yield dependency wrapping the whole request, or explicitly in service functions. Be consistent across the codebase. See Python asyncio for async session considerations.
Don't Do Heavy Work in Sync Dependencies
Sync (def) dependencies run in a thread pool; async (async def) dependencies run on the event loop and must not block it. Match the dependency style to the libraries you call.
Common Mistakes
Creating Engines or Clients per Request
Create the engine once (module level or lifespan) and create only sessions per request.
Blocking Calls in async Dependencies
Calling requests.get() or a synchronous database driver inside an async def dependency blocks the event loop for every concurrent request. Use async libraries, or make the dependency a plain def, which runs in a thread pool.
Forgetting to Clear Overrides in Tests
dependency_overrides is global to the app object. Tests that set overrides without clearing them leak fakes into later tests. Use fixtures that set and clear them.
FAQ
What is Depends in FastAPI?
Depends marks a parameter as a dependency: FastAPI calls the given callable (resolving its own parameters and sub-dependencies), then passes the result to your endpoint. It powers database sessions, authentication, permissions, shared query parameters, and more, with automatic validation and OpenAPI documentation.
Are dependencies called once per request?
Yes, by default. Each dependency is executed at most once per request, and its result is reused wherever it's declared in that request's dependency graph. Pass use_cache=False to force separate calls.
How do I share a database session across dependencies and the endpoint?
Declare the same session dependency (for example SessionDep) wherever it's needed. Because of per-request caching, all of them receive the same session instance, so queries participate in the same transaction.
Should I use dependencies or middleware for authentication?
Dependencies, usually. They integrate with OpenAPI security schemes, can be applied selectively per route or router, and give endpoints a typed user object. Middleware suits concerns that must apply to every request uniformly, such as request IDs, CORS, or logging.
Related Topics
- FastAPI — The framework overview
- FastAPI Authentication — Security dependencies and scopes
- FastAPI Testing — Overriding dependencies in tests
- FastAPI & Pydantic Models — Request and response validation
- Python asyncio — Async vs sync dependencies
- SQLAlchemy — Sessions provided by dependencies