How to use middleware with Django Ninja
A quick guide on using middleware in APIs built with Django Ninja. Learn how to add third-party middleware and how to implement your own.


Django Ninja is a modern web framework for building APIs with Django and is heavily inspired by FastAPI. While it shares many features with FastAPI, such as automatic OpenAPI documentation and type validation, it differs in how middleware is handled.
Limitations
Django Ninja currently doesn’t have its own middleware system and instead relies on Django’s middleware, configured via the MIDDLEWARE
setting.
Middleware configured this way applies globally to the entire Django application, including Django Ninja APIs.
Unfortunately, it’s not possible to apply middleware only to a specific Django Ninja API or router.
There is an open issue to add middleware support to Django Ninja directly, but it’s not yet clear if and when it will be implemented.
Adding middleware
To add middleware to your Django application, you simply add its fully qualified class name to the MIDDLEWARE
list in your Django settings.
In the below example, we’ve added the ApitallyMiddleware
to the top of the list.
# settings.py
MIDDLEWARE = [
"apitally.django.ApitallyMiddleware", # Example
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Note that the order of middleware in the MIDDLEWARE
list matters.
Django processes middleware in the order listed for incoming requests and in reverse order for outgoing responses.
This creates an “onion” effect where each middleware wraps the next one.
For example, if you’re using authentication middleware, it should come before any middleware that depends on user information.
Similarly, compression middleware like GZipMiddleware
should come after middleware that might modify the response body, but before caching middleware.
The Django documentation provides detailed guidance on middleware ordering. As a general rule, place security-related middleware near the top, authentication middleware in the middle, and response-modifying middleware toward the bottom of the list.
Building your own middleware
A Django middleware is simply a Python class with an __init__
method that accepts a get_response
callable, and a __call__
method that processes requests and responses.
# custom_middleware.py
from typing import Callable
from django.http import HttpRequest, HttpResponse
class CustomMiddleware:
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None:
self.get_response = get_response
def __call__(self, request: HttpRequest) -> HttpResponse:
# Code executed before the view (and later middleware)
print(f"Processing request to {request.path}")
response = self.get_response(request)
# Code executed after the view (and earlier middleware)
print(f"Response status: {response.status_code}")
return response
You can also add optional methods like process_view
, process_exception
, and process_template_response
for more specific hooks into Django’s request/response cycle.
See the Django documentation for details.
Keep in mind that middleware runs on every request, so avoid heavy operations that could impact your API’s response times.
Finally, add your middleware to the MIDDLEWARE
setting using its fully qualified class name:
# settings.py
MIDDLEWARE = [
"myapp.middleware.CustomMiddleware",
# ... other middleware
]