Skip to main content
Dependency Injection (DI) is one of the most important patterns used in production-grade FastAPI applications, yet it’s often misunderstood because it comes bundled with related (but different) concepts. Before you start using Depends(), it helps to understand what a dependency actually is, why injecting it from outside is better than creating it internally, and how this relates to the Inversion of Control principle. Once those ideas click, Depends() becomes the most natural thing in the world.

What is a Dependency?

A dependency is any object, resource, or service that a function or class needs in order to do its work. In a FastAPI application, your endpoints often depend on things like:
  • A database session
  • A repository or service class
  • The currently authenticated user
  • Configuration settings
  • An external API client
  • A logger
A dependency is simply something your code needs to do its job.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern where a function or object receives its dependencies from an external source rather than creating them internally.

Without DI — tightly coupled

With DI — loosely coupled

The service no longer creates its own dependency. This makes the code:
  • Loosely coupled — swapping Printer for a MockPrinter in tests requires no changes to StudentService
  • Easier to test — inject a mock or stub at will
  • Easier to reuse — the same service works with any compatible printer

What is Inversion of Control?

Inversion of Control (IoC) is a design principle where the responsibility for creating objects and managing application flow is delegated to a framework rather than your own code. Without IoC, your application code creates everything:
With IoC, the framework takes over:
No. They are related but different concepts.
  • IoC is a design principle — it describes who is in control of the application’s workflow and object lifecycle.
  • DI is a design pattern — it describes how dependencies are provided to objects.
DI is one of the most common techniques used to achieve IoC, but IoC can exist without DI (using callbacks or events) and DI can exist without IoC (manual injection where your code still controls creation).
Yes. Even before you use Depends(), FastAPI already injects values into your endpoint functions:
For GET /students/101?active=false, FastAPI automatically injects:
  • student_id = 101 (from the path)
  • active = False (from the query string)
You never write this extraction code yourself. Depends() extends this same mechanism to inject higher-level application resources.

FastAPI’s Depends()

FastAPI provides a built-in DI system through the Depends() function. When a request arrives, FastAPI:
  1. Resolves the dependency (calls the provider function)
  2. Injects the result into the endpoint
  3. Cleans it up after the request completes (if you use yield)

Common Dependency Patterns

Database Session Dependency

The yield pattern ensures the session is always closed, even if the endpoint raises an exception.

Current User Dependency

Reusable Typed Dependencies with Annotated

Use Annotated to define a reusable dependency alias once and use it everywhere:

Dependency Resolution Flow

FastAPI resolves dependencies in the correct order — if get_service() depends on get_db(), FastAPI calls get_db() first, then passes the result to get_service(), then injects the service into your endpoint. You never manage this order manually.

What FastAPI Can Inject


Benefits

Using Depends() across your application delivers:
  • Loose coupling — endpoints don’t know how their dependencies are created
  • Testability — override any dependency with a mock in tests using app.dependency_overrides
  • Reusability — define a dependency once, inject it in dozens of endpoints
  • Automatic lifecycle managementyield dependencies are always cleaned up
  • Composability — dependencies can themselves depend on other dependencies
To override a dependency in tests:
This replaces the real database with a test fixture for the duration of your tests — no changes to your production code required.