Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 29

FastAPI Dependency Injection vs. Django/DRF Patterns

Recommended resources

Hello! Welcome to the sixth module of our course. In the previous module, we focused on integrating a database with our FastAPI application, culminating in the last lesson where you learned to manage database schema changes using Alembic, a process very similar to Django's migration system.

Now, we're shifting gears from the database layer to a core feature of the FastAPI framework itself: its powerful Dependency Injection system.

This lesson will address the learning outcome: Explain FastAPI's dependency injection (DI) system and contrast it with Django's middleware and DRF's permission/mixin patterns.

This is a fundamental concept that unlocks much of FastAPI's power, enabling you to write clean, reusable, and testable code. For someone with your background, the key is to understand how DI replaces and often improves upon familiar patterns from Django and DRF. We'll draw direct comparisons to concepts like middleware, permission classes, and even self.request to ground your understanding.

1. What is Dependency Injection?

Before diving into FastAPI's implementation, let's clarify what "Dependency Injection" (DI) means in general software design. At its heart, DI is a pattern that promotes decoupling—separating the code that uses a resource (a "client") from the code that creates that resource (a "service").

Instead of a function creating its own dependencies (like a database connection or a user object), those dependencies are "injected" into it from an external source.

To see this principle in action with a simple Python example, let's watch the first part of a video from ArjanCodes.

Should You Use Dependency Injection Frameworks?

This video, 'Should You Use Dependency Injection Frameworks?', provides an excellent, framework-agnostic explanation of the core idea.

Watch the first four minutes (00:00 - 04:07). Focus on the simple example of passing a user object to a function versus creating it inside. This demonstrates the basic principle of separating creation from usage.

As the video illustrates, the main benefits of this separation are:

  • Flexibility: The function is no longer tied to a single, hardcoded resource.
  • Reusability: The function can be reused with different objects that fit the required "shape" or interface.
  • Testability: It's easier to test the function by passing it a mock object instead of a real one.

2. Dependency Injection in FastAPI: The Depends Function

FastAPI elevates this simple pattern into a first-class feature of the framework. It provides a way to declare dependencies directly in the signature of your path operation functions.

The key to this system is the Depends function.

A dependency in FastAPI is simply a callable (like a function or a class instance) that FastAPI will execute before your path operation function. The return value of this dependency is then "injected" as an argument into your path operation.

Let's look at a classic example: managing a database session.

Re-Doing the Django Tutorial With FastAPI And React

The article 'Re-Doing the Django Tutorial With FastAPI And React' walks through a scenario you'll find familiar: needing a database session in an endpoint. It perfectly introduces DI as the solution.

Read the sections 'Building the Endpoints', 'What is dependeny injection?', and the code blocks that follow. Focus on: How the endpoints initially have an undefined db variable. The excellent analogy: self.request in Django's Class-Based Views is a dependency injection pattern! The structure of the get_db dependency function, which uses yield to manage the session's lifecycle (setup and teardown). How db = Depends(get_db) is used in the endpoint's signature to inject the session.

The get_db function is a perfect example of a dependency.

  • It's a regular Python function.
  • It handles the logic of creating and, crucially, closing a database session.
  • FastAPI calls it for you, and its yielded value (db) is passed to your index or question_detail function.

This means your endpoint's code can focus purely on its business logic, assuming a database session will be provided.


3. Contrasting DI with Django and DRF Patterns

Now for the core of the lesson: mapping this new concept to the patterns you already know. FastAPI's DI system is a unified mechanism that handles tasks you'd use several different features for in Django/DRF.

Task in Web APIs Django / DRF Approach FastAPI Approach
Run code on every request Middleware (settings.py) Global Dependency (on the FastAPI app instance) or Middleware
Run code for a group of endpoints Custom Middleware, Mixins, or decorating every view. Router-level Dependency (on an APIRouter instance)
Check user permissions/authentication DRF Permission/Authentication classes (e.g., permission_classes = [IsAuthenticated]) Dependency that validates a token and returns a user object (or raises HTTPException).
Fetch a common object from DB Mixin or a base class method (e.g., get_object() in DRF Generic Views). Dependency that takes an ID, fetches the object, and returns it (or raises a 404 HTTPException).
Provide shared utilities/data to a view self.request, self.kwargs, context processors. Dependencies that can return any value, which is then passed as a function argument.

Let's break down these comparisons.

DI vs. Middleware

In Django, middleware is a powerful tool for processing every request and response globally. FastAPI has middleware too, but you'll find yourself using dependencies for many of the same tasks.

The key difference is granularity.

FastAPI for Django Programmers: Dependency Injections ... - Medium

This article, 'FastAPI for Django Programmers: Dependency Injections...', explicitly compares the two.

Read the 'Middleware' section. The author makes a critical point: Dependency Injection deals with the majority of tasks you might use middleware for, but with more granular control.

  • Middleware is generally all-or-nothing. It runs for every single endpoint defined in your application.
  • Dependencies can be applied precisely where they are needed:
    • Globally (like middleware).
    • To all endpoints in a specific router (e.g., all endpoints under /users/).
    • To a single, specific path operation.

This granularity makes your code more explicit and easier to reason about. You can see exactly what an endpoint depends on just by looking at its function signature.

DI vs. DRF Permission & Authentication Classes

This is one of the most powerful comparisons. In DRF, you might protect a view like this:




# DRF way
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class MyView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):



        # Here, we know request.user is an authenticated user
        content = {'message': f'Hello, {request.user.username}!'}
        return Response(content)

The IsAuthenticated class checks for a valid user and raises an exception if one isn't found. The request.user object is populated separately by the authentication backend.

In FastAPI, you combine both actions—checking permission and retrieving the data—into a single dependency.

FastAPI Auth with Dependency Injection: Good vs. Bad Implementation
This image compares a bad practice (left) where authentication logic clutters the endpoint, with a good practice (right) where the logic is cleanly encapsulated in a `require_user` dependency and injected into the endpoint.

The "Good" example on the right shows the FastAPI way.

  1. A dependency (e.g., require_user) is created. This dependency would contain the logic to read the Authorization header, validate the token, and fetch the user from the database.
  2. If the token is invalid, the dependency raises an HTTPException.
  3. If the token is valid, the dependency returns the user object.
  4. The endpoint receives this user object directly as a function argument (user: User = Depends(require_user)).

This pattern is incredibly powerful because the dependency not only protects the endpoint but also provides it with the necessary data in a single, clean step.

DI vs. DRF Mixins and Generic Views

In DRF, you often use mixins (ListModelMixin, RetrieveModelMixin) and generic views (ListAPIView) to reuse common logic, like fetching an object. A typical get_object method in a generic view fetches an object based on a URL kwarg (pk) and raises a 404 if it's not found.

You can achieve the exact same thing with a dependency.

FastAPI for Django Programmers: Dependency Injections ... - Medium

Let's revisit the 'FastAPI for Django Programmers' article, which provides a perfect example of a get_record dependency that mimics DRF's get_object.

Read the section under the 'Dependency Injection' heading, focusing on the get_record code example. Notice how it takes a person_id, fetches the record, raises a 404 HTTPException if it's not found, and returns the record otherwise.

This approach replaces the inheritance-based model of mixins with a more flexible composition-based model. You are "composing" your endpoint with functionality by injecting dependencies, rather than "inheriting" functionality from a base class.


Key Takeaways

To summarize, FastAPI's Dependency Injection system is a unified and flexible mechanism for managing shared logic and resources.

  • Core Idea: It separates the creation of a resource from its use, injecting dependencies as function arguments using Depends.
  • vs. Middleware: DI offers more granular control, allowing you to apply logic to specific endpoints or routers, whereas middleware is typically global.
  • vs. DRF Permissions: DI combines permission checking and data retrieval into one step. A single dependency can both protect an endpoint and provide it with the authenticated user object.
  • vs. DRF Mixins: DI favors composition over inheritance. You inject functionality via function arguments instead of inheriting it from base classes, leading to more flexible and testable code.

In essence, you can think of DI as FastAPI's answer to a whole suite of features in Django/DRF, rolled into one consistent and powerful pattern.

Preview of the Next Lesson:

Now that you understand the "what" and "why" of dependency injection, the next lesson will focus on the "how". We will get hands-on and learn to create and use simple function-based dependencies with Depends(). We'll build practical dependencies for tasks like pagination and API key authentication.