The Adaptable Backend: Python RestAPI Dependency Injection

By Hendrix Roa
December 6, 2025
3 min read
Posted in The Adaptable Backend: Python
The Adaptable Backend: Python RestAPI Dependency Injection

In the previous article, we set up a decoupled Python backend using FastAPI. However, our controller logic was still somewhat coupled to the API layer. Today, we’re going to introduce Dependency Injection to fully separate our business logic from the web framework.

In Python, we have a powerful DI libraries, but given the nature of the application and the decoupled architecture, we will use: dependency-injector.

Why Dependency Injection?

Dependency Injection allows us to:

  1. Decouple components: Our business logic doesn’t need to know how its dependencies are created.
  2. Improve testability: We can easily mock dependencies (like databases or external APIs) during testing.
  3. Centralize configuration: All wiring happens in one place (the Container).

While FastAPI has its own built-in DI system (Depends), using a framework-agnostic library like dependency-injector ensures our core business logic remains independent of FastAPI itself.

Installing dependency-injector

First, let’s add the library using uv:

Install dependency-injector
uv add dependency-injector

Creating the Core Layer

We want our business logic to live in src/core, completely unaware of FastAPI.

1. The Core Controller

Let’s create a pure Python controller that handles our business logic.

src/core/features/note/note_controller.py
from .note_dto import NoteDto
class NoteController:
def get_notes(self) -> list[NoteDto]:
return [
NoteDto(id=1, content="Hello 1 - from core controller"),
NoteDto(id=2, content="Hello 2 - from core controller"),
]

2. The DI Container

Now, let’s create the container that will manage our dependencies. This is the Python equivalent of the Awilix container.

src/core/container/container.py
from dependency_injector import containers, providers
from src.core.features.note.note_controller import NoteController
class Container(containers.DeclarativeContainer):
note_controller = providers.Singleton(NoteController)
container = Container()

The NoteDto should be moved from src/apps/rest_api/frameworks/fastapi/rest_controllers/note_dto.py to src/core/features/note/note_dto.py, since DTOs belong in the core layer where the business logic resides.

Wiring it into FastAPI

Now we need to connect our framework-agnostic core to our FastAPI application. We’ll update our NoteRestController to inject the controller from the container.

src/apps/rest_api/frameworks/fastapi/rest_controllers/note_rest_controller.py
from fastapi import APIRouter
from .note_dto import NoteDto
from src.core.features.note.note_dto import NoteDto
from src.core.features.note.note_controller import NoteController
from src.core.container.container import container
class NoteRestController:
def __init__(self) -> None:
self.note_controller: NoteController = container.note_controller()
self.router = APIRouter(prefix="/notes", tags=["notes"])
self._setup_routes()
def _setup_routes(self) -> None:
self.router.add_api_route(
"/",
self.get_notes,
methods=["GET"],
response_model=list[NoteDto],
)
async def get_notes(self) -> list[NoteDto]:
return [
NoteDto(id=1, content="Hello 1"),
NoteDto(id=2, content="Hello 2"),
]
return self.note_controller.get_notes()
note_controller = NoteRestController()
router = note_controller.router

The Result

Now, when a request comes in:

  1. FastAPI receives the request at /notes.
  2. NoteRestController (the Adapter) receives the call.
  3. It delegates to self.note_controller (the Core Logic).
  4. NoteController returns the data.

We have successfully achieved Clean Architecture! Our core logic is pure Python, and FastAPI is just a delivery mechanism.

Conclusion

By using dependency-injector, we’ve replicated the flexible, decoupled architecture of our Node.js application in Python. We’re not just writing “scripts”; we’re building a robust, maintainable software system.

In the next article, we’ll look at adding persistence with a database, maintaining this strict separation of concerns.

Comments

Loading comments...

You Might Also Like