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

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:

Class-Based and Parameterized Dependencies

Path, Router, and App Dependencies

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

References