Middleware¶
Django-Bolt provides middleware for cross-cutting concerns like CORS and rate limiting. This guide covers the built-in middleware and how to use it.
CORS middleware¶
Per-route CORS¶
Apply CORS to specific endpoints:
from django_bolt.middleware import cors
@api.get("/api/data")
@cors(origins=["https://example.com"], credentials=True)
async def get_data():
return {"data": "value"}
CORS options¶
@cors(
origins=["https://example.com", "https://app.example.com"],
methods=["GET", "POST", "PUT", "DELETE"],
headers=["Content-Type", "Authorization"],
credentials=True,
max_age=3600, # Preflight cache duration
)
Global CORS¶
Configure CORS for all endpoints in settings.py:
# Allow specific origins
BOLT_CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://app.example.com",
]
# Allow all origins (development only!)
BOLT_CORS_ALLOW_ALL_ORIGINS = True
# Additional settings
BOLT_CORS_ALLOW_CREDENTIALS = True
BOLT_CORS_ALLOW_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]
BOLT_CORS_ALLOW_HEADERS = ["Content-Type", "Authorization", "X-Requested-With"]
BOLT_CORS_EXPOSE_HEADERS = ["X-Total-Count", "X-Page-Count"]
BOLT_CORS_MAX_AGE = 86400 # 24 hours
Rate limiting¶
Per-route rate limiting¶
from django_bolt.middleware import rate_limit
@api.get("/api/search")
@rate_limit(rps=10, burst=20)
async def search(q: str):
return {"results": []}
Parameters:
rps- Requests per second allowedburst- Maximum burst size (allows short spikes)
How it works¶
Django-Bolt uses a token bucket algorithm:
- Tokens are added at
rpsper second - Each request consumes one token
- The bucket holds up to
bursttokens - If no tokens are available, the request is rejected with 429 Too Many Requests
Rate limit response¶
When rate limited, the response includes:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
Skipping middleware¶
Disable specific middleware for an endpoint:
from django_bolt.middleware import skip_middleware
@api.get("/health")
@skip_middleware("cors", "rate_limit")
async def health():
return {"status": "ok"}
Or skip all middleware:
Compression¶
Django-Bolt automatically compresses responses. Disable for specific endpoints:
from django_bolt.middleware import no_compress
@api.get("/stream")
@no_compress
async def stream():
# Streaming responses should not be compressed
return StreamingResponse(generate())
Compression settings¶
Configure in settings.py:
from django_bolt import CompressionConfig
# In your api.py
api = BoltAPI(
compression=CompressionConfig(
backend="brotli", # "brotli", "gzip", or "zstd"
minimum_size=1000, # Only compress responses > 1KB
gzip_fallback=True, # Fall back to gzip if client doesn't support brotli
)
)
Custom middleware¶
Create custom middleware by defining a middleware function:
from django_bolt.middleware import middleware
@middleware
async def timing_middleware(request, call_next):
import time
start = time.time()
response = await call_next(request)
duration = time.time() - start
response.headers["X-Response-Time"] = f"{duration:.3f}s"
return response
@api.get("/timed")
@timing_middleware
async def timed_endpoint():
return {"status": "ok"}
Django middleware integration¶
Django-Bolt seamlessly integrates with Django's middleware system, allowing you to use existing Django middleware with your API endpoints.
Quick start¶
The simplest approach is to use the django_middleware parameter, which loads middleware from your Django settings.MIDDLEWARE:
from django_bolt import BoltAPI
# Load all middleware from settings.MIDDLEWARE
api = BoltAPI(django_middleware=True)
Configuration options¶
The django_middleware parameter accepts several configuration formats:
# Load all middleware from settings.MIDDLEWARE
api = BoltAPI(django_middleware=True)
# Disable Django middleware
api = BoltAPI(django_middleware=False)
# Load specific middleware only
api = BoltAPI(django_middleware=[
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
])
# Exclude specific middleware
api = BoltAPI(django_middleware={
"exclude": ["django.middleware.csrf.CsrfViewMiddleware"]
})
# Include only specific middleware
api = BoltAPI(django_middleware={
"include": [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
]
})
Using DjangoMiddleware wrapper¶
For wrapping individual middleware classes directly:
from django_bolt import BoltAPI, DjangoMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.auth.middleware import AuthenticationMiddleware
api = BoltAPI(
middleware=[
DjangoMiddleware(SessionMiddleware),
DjangoMiddleware(AuthenticationMiddleware),
]
)
You can also use import path strings:
api = BoltAPI(
middleware=[
DjangoMiddleware("django.contrib.sessions.middleware.SessionMiddleware"),
DjangoMiddleware("myapp.middleware.CustomMiddleware"),
]
)
Using DjangoMiddlewareStack¶
When using multiple Django middleware, DjangoMiddlewareStack is more efficient as it performs a single request conversion instead of one per middleware:
from django_bolt import BoltAPI
from django_bolt.middleware import DjangoMiddlewareStack
from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.auth.middleware import AuthenticationMiddleware
api = BoltAPI(
middleware=[
DjangoMiddlewareStack([
SessionMiddleware,
AuthenticationMiddleware,
])
]
)
Accessing Django request attributes¶
Django middleware sets attributes on the request that are automatically synced to the Bolt request:
@api.get("/profile")
async def profile(request):
# User from AuthenticationMiddleware (async)
user = await request.auser()
# Session from SessionMiddleware
session = request.session
theme = await session.aget("theme", "light")
# Messages from MessageMiddleware
messages = request.state.get("_messages")
return {
"username": user.username if user.is_authenticated else "anonymous",
"authenticated": user.is_authenticated,
"session_key": session.session_key,
"theme": theme,
}
Available synced attributes:
| Attribute | Source | Access |
|---|---|---|
| User (async) | AuthenticationMiddleware | await request.auser() |
| User (sync) | AuthenticationMiddleware | request.user |
| Session | SessionMiddleware | request.session |
| Messages | MessageMiddleware | request.state["_messages"] |
| META | All middleware | request.state["META"] |
| CSRF token | CsrfViewMiddleware | request.state["_csrf_token"] |
Performance notes¶
Django-Bolt optimizes middleware execution with a three-tier system:
- Django built-in middleware - Executed directly without thread pool overhead (fastest)
- Third-party middleware with hooks - Wrapped in
sync_to_asyncfor safety - Custom
__call__middleware - Executed as a chain via singlesync_to_asynccall
The DjangoMiddlewareStack automatically categorizes your middleware for optimal performance.
Middleware order¶
Middleware is executed in the order specified:
- CORS (handles preflight requests)
- Rate limiting
- Authentication (via
auth=parameter) - Guards (via
guards=parameter) - Your handler
For responses, the order is reversed.
Performance¶
Django-Bolt's middleware runs in Rust where possible:
- CORS preflight handling
- Rate limiting with token bucket
- Response compression
This means these operations don't acquire the Python GIL, enabling higher throughput.