> ## Documentation Index
> Fetch the complete documentation index at: https://fastapi2day.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Dependency Injection in Python: Decoupling Your Code

> Learn the Dependency Injection pattern, understand why loose coupling matters for testability, and see how FastAPI automates DI with Depends().

Dependency Injection (DI) is a design pattern that fundamentally changes how you think about object relationships in your code. Instead of letting an object create its own dependencies internally, you **supply them from outside**. This single shift makes components loosely coupled, trivially replaceable, and far easier to test. DI is the backbone of modern application architectures, and FastAPI has first-class support for it through its `Depends()` function — making your API endpoints clean, composable, and side-effect-free.

## What is a Dependency?

A **dependency** is any external object that another object needs to do its job. Common examples:

* A database connection or session
* A repository (data access layer)
* A logger
* An email service
* A configuration object
* An authentication/authorisation service

## Without Dependency Injection

When a class creates its own dependencies internally, it becomes **tightly coupled** to that specific implementation:

```python theme={null}
class Engine:
    pass

class Car:
    def __init__(self):
        self.engine = Engine()   # Car creates its own Engine — tight coupling
```

**Problems with this approach:**

* You cannot test `Car` without also instantiating `Engine`.
* Replacing `Engine` with a `MockEngine` or `ElectricEngine` requires modifying `Car`'s source code.
* Behaviour is hidden — the caller cannot control what `Car` uses internally.

## With Dependency Injection

The dependency is **supplied from outside** — the object receives it rather than creating it:

```python theme={null}
class Engine:
    pass

class Car:
    def __init__(self, engine):    # Engine is injected, not created
        self.engine = engine

# Caller controls which engine is used
engine = Engine()
car = Car(engine)
```

Now `Car` only uses the engine — it has no knowledge of how to create one. You can pass any compatible object, including a test double.

## Real-World Example: Repository & Service

### Without DI — Tightly Coupled

```python theme={null}
class StudentRepository:
    def get_all(self):
        return ["Alice", "Bob"]

class StudentService:
    def __init__(self):
        self.repo = StudentRepository()   # tightly coupled

    def list_students(self):
        return self.repo.get_all()
```

### With DI — Loosely Coupled

```python theme={null}
class StudentRepository:
    def get_all(self):
        return ["Alice", "Bob"]

class StudentService:
    def __init__(self, repository):   # repository is injected
        self.repo = repository

    def list_students(self):
        return self.repo.get_all()

# Compose the objects at the call site
repo    = StudentRepository()
service = StudentService(repo)

print(service.list_students())   # ['Alice', 'Bob']
```

Later, swapping the data source requires no changes to `StudentService`:

```python theme={null}
# Swap in a mock for testing — no code changes needed in StudentService
service = StudentService(MockStudentRepository())
```

## Benefits of Dependency Injection

| Benefit             | Description                                                         |
| ------------------- | ------------------------------------------------------------------- |
| **Loose coupling**  | Components depend on abstractions, not concrete implementations     |
| **Testability**     | Inject mocks or fakes in tests without touching production code     |
| **Replaceability**  | Swap a database, logger, or service by changing the injected object |
| **Reusability**     | The same component works with different implementations             |
| **Maintainability** | Smaller, focused classes that are easier to understand and change   |

## Types of Dependency Injection

### Constructor Injection (Most Common)

```python theme={null}
class UserService:
    def __init__(self, database):
        self.database = database
```

### Setter Injection

```python theme={null}
class UserService:
    def set_database(self, database):
        self.database = database
```

### Method Injection

```python theme={null}
class UserService:
    def get_user(self, user_id: int, database):
        return database.find(user_id)
```

Constructor injection is strongly preferred because it makes dependencies **explicit** and ensures the object is always in a valid state from the moment it is created.

## FastAPI's Dependency Injection

FastAPI automates dependency injection for your API routes using the `Depends()` function:

```python theme={null}
from fastapi import Depends, FastAPI

app = FastAPI()

def get_repository():
    return StudentRepository()

@app.get("/students")
def get_students(repo=Depends(get_repository)):
    return repo.get_all()
```

FastAPI calls `get_repository()` automatically before executing `get_students`, and injects the result as the `repo` parameter.

### Chaining Dependencies

You can chain dependencies — services that depend on repositories, for example:

```python theme={null}
def get_repository():
    return StudentRepository()

def get_service(repo=Depends(get_repository)):
    return StudentService(repo)

@app.get("/students")
def get_students(service=Depends(get_service)):
    return service.list_students()
```

FastAPI resolves the entire dependency graph automatically, creating each dependency in the correct order.

<Tip>
  FastAPI also supports **generator-based dependencies** with `yield` for resources that need clean-up (like database sessions):

  ```python theme={null}
  def get_db():
      db = SessionLocal()
      try:
          yield db
      finally:
          db.close()

  @app.get("/users")
  def get_users(db=Depends(get_db)):
      return db.query(User).all()
  ```

  The `finally` block always runs after the endpoint finishes, guaranteeing the session is closed. This is the context manager pattern applied to dependency injection.
</Tip>
