Django Bolt
Django Bolt is a high-performance Django framework that bypasses the Python GIL using Rust, delivering 300k+ RPS for simple endpoints.
Overview
Django Bolt combines Django's ORM and ecosystem with Rust's performance. Built on top of:
- Hyper (Rust HTTP server)
- Tokio (async runtime)
- msgspec (5-10x faster than Pydantic)
Key features:
- Async ORM - Native async/await support for Django ORM
- Built-in WebSocket - Real-time communication without channels
- SSE Streaming - Server-Sent Events for live updates
- JWT Authentication - Built-in auth with refresh token rotation
- msgspec Serializers - Fast schema validation and serialization
- OpenAPI Docs - Auto-generated at
/docs
Installation
pip install django-bolt
Add to INSTALLED_APPS:
INSTALLED_APPS = [
...
"django_bolt",
]
Quick Start
# myapp/api.py
from django_bolt import BoltAPI
api = BoltAPI()
@api.get("/")
async def hello():
return {"message": "Hello, World!"}
@api.get("/users")
async def list_users():
users = await User.objects.all()
return [{"id": u.id, "username": u.username} for u in users]
Mount in urls.py:
# myproject/urls.py
from django.urls import path
from myapp.api import api
urlpatterns = [
path("api/", api.urls),
]
Run:
python manage.py runbolt --dev
Routing
Basic Routes
@api.get("/users")
@api.post("/users")
@api.put("/users/{pk}")
@api.delete("/users/{pk}")
@api.patch("/users/{pk}")
Path Parameters
@api.get("/users/{user_id}")
async def get_user(user_id: int):
user = await User.objects.aget(id=user_id)
return {"id": user.id, "username": user.username}
Query Parameters
@api.get("/users")
async def list_users(page: int = 1, limit: int = 10):
users = await User.objects.all()[limit*(page-1):limit*page]
return [{"id": u.id} for u in users]
Request Body
import msgspec
class UserCreate(msgspec.Struct):
username: str
email: str
@api.post("/users")
async def create_user(data: UserCreate):
user = await User.objects.acreate(username=data.username, email=data.email)
return {"id": user.id}, 201
Middleware
from django_bolt.middleware import CompressionMiddleware
api.add_middleware(CompressionMiddleware())
Class-Based Views
ViewSet
from django_bolt.views import ViewSet, route
class UserViewSet(ViewSet):
@route.get("/users")
async def list(self, request):
users = await User.objects.alist()
return {"users": [{"id": u.id, "username": u.username} for u in users]}
@route.get("/users/{pk}")
async def retrieve(self, request, pk: int):
user = await User.objects.aget(id=pk)
return {"id": user.id, "username": user.username}
@route.post("/users")
async def create(self, request):
data = await request.json()
user = await User.objects.acreate(**data)
return {"id": user.id}
@route.put("/users/{pk}")
async def update(self, request, pk: int):
data = await request.json()
user = await User.objects.aget(id=pk)
for k, v in data.items():
setattr(user, k, v)
await user.asave()
return {"id": user.id}
@route.delete("/users/{pk}")
async def destroy(self, request, pk: int):
user = await User.objects.aget(id=pk)
await user.adelete()
return {"deleted": True}
api.register_viewset(UserViewSet, prefix="/api")
ModelViewSet
from django_bolt.views import ModelViewSet
from django.contrib.auth import get_user_model
from django_bolt.serializers import ModelSerializer
User = get_user_model()
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "email"]
class UserModelViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
api.register_viewset(UserModelViewSet, prefix="/api")
OpenAPI / API Documentation
Django Bolt auto-generates OpenAPI 3.1 documentation.
Access Docs
- Swagger:
/docs
- ReDoc:
/redoc
- Scalar:
/scalar
- RapidDoc:
/rapiddoc
Configure OpenAPI
from django_bolt import BoltAPI
from django_bolt.openapi import OpenAPIInfo
api = BoltAPI(
info=OpenAPIInfo(
title="My API",
version="1.0.0",
description="API description",
)
)
Include/Exclude from Schema (v0.10.2+/0.11.0+)
@api.get("/internal", include_in_schema=False)
async def internal_handler():
return {"secret": True}
Testing
Test Client
from django_bolt.test import AsyncAPITestClient
class UserAPITest(AsyncAPITestClient):
async def test_create_user(self):
response = await self.post(
"/api/users",
json={"username": "testuser", "email": "test@example.com"}
)
self.assertEqual(response.status_code, 201)
data = await response.json()
self.assertEqual(data["username"], "testuser")
async def test_get_user(self):
user = await User.objects.acreate(username="testuser", email="test@example.com")
response = await self.get(f"/api/users/{user.id}")
self.assertEqual(response.status_code, 200)
Note: TestClient shares DB connection with handler threads for accurate concurrency testing.
Performance Benchmarks
Reference Setup
- Hardware: AMD Ryzen 5 5600G (6 cores / 12 threads), 16 GB RAM, Linux
- Server:
python manage.py runbolt --processes 8 (8 processes × 1 Actix worker)
- Load generator: bombardier, C=100, N=100000, loopback on same machine
- Baseline comparisons: Plain Django views, DRF, FastAPI, Litestar, JavaScript runtimes (see sources)
- Run-to-run variance: ±5% (gaps under ~10% are ties)
Important: The load generator shares the 12 cores with the 8 server processes, so numbers are a floor, not a ceiling. Absolute numbers are hardware-specific—always publish the conditions with the number.
Key Results
| Endpoint Type | Req/s | What it measures |
|---|
JSON hello-world (GET /) | 311,270 | Routing + JSON serialization overhead |
| 10 KB JSON (sync handler) | 187,186 | Response serialization bottleneck |
| 10 KB JSON (async handler) | 184,059 | Async overhead negligible at scale |
| 10-row ORM query (SQLite) | 20,963–26,694 | Django ORM executor + SQLite file lock |
| JWT auth + DB load user | 40,312 | Rust JWT verify + ORM user fetch |
| Full middleware stack | 8,744 | Sessions, CSRF, auth, messages, CSP |
What These Numbers Do NOT Tell You
- Not per-request latency figures — p50/p99 are available for select endpoints but these RPS numbers measure throughput under load
- Excludes database time for ORM benchmarks — measured with SQLite (single-writer file lock is the bottleneck); PostgreSQL uses more threads
- Does not include network latency — loopback test on same machine
- Not a measure of developer productivity — pure throughput comparison
Cross-Framework Comparison (same machine, one process each, PostgreSQL)
Source: https://github.com/FarhanAliRaza/python-api-frameworks-benchmark (2026-08-30)
| Endpoint | Django-Bolt | Litestar + uvicorn | FastAPI + uvicorn |
|---|
| 1 KB JSON | 43,541 | 15,477 | 7,517 |
| 10 KB JSON | 27,610 | 12,925 | 1,805 |
| 10 rows from PostgreSQL | 2,881 | 1,321 | 1,237 |
| JWT auth + load user | 4,478 | 1,155 | 941 |
Reproduce
git clone https://github.com/dj-bolt/django-bolt.git && cd django-bolt
uv sync && just build
go install github.com/codesenberg/bombardier@latest
just save-bench # → python/benchmark/BENCHMARK.md
Source: https://bolt.farhana.li/benchmarks/ | https://github.com/dj-bolt/django-bolt/blob/master/python/benchmark/BENCHMARK.md
Configuration
Settings
# settings.py
# Django-Bolt Configuration
BOLT = {
"HOST": "0.0.0.0",
"PORT": 8000,
"PROCESSES": 4,
"BACKLOG": 2048,
"KEEP_ALIVE": 30,
"DEBUG": False,
"EMIT_SIGNALS": False,
}
# JWT Configuration
JWT_SECRET_KEY = "your-secret-key"
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION = 3600
# File Upload Settings
from django_bolt import FileSize
BOLT_MAX_UPLOAD_SIZE = FileSize.MB_50
BOLT_MEMORY_SPOOL_THRESHOLD = 5 * 1024 * 1024
# CORS Configuration
BOLT_CORS = {
"allow_origins": ["https://example.com"],
"allow_methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["*"],
"allow_credentials": True,
}
Production Server: runbolt
Worker Recycling (v0.11.0+)
# Recycle workers after memory threshold
python manage.py runbolt --max-rss 512000
# Limit worker lifetime
python manage.py runbolt --workers-lifetime 3600
# Auto-respawn failed workers
python manage.py runbolt --respawn-failed-workers
Development Mode (v0.11.0+)
# Native Rust reloader
python manage.py runbolt --dev
# Custom reload directory
python manage.py runbolt --dev --reload-dir src/
Best Practices
Performance
- Use async ORM methods - Always use
aget(), alist(), acreate() in handlers
- Avoid N+1 queries - Use
select_related() for ForeignKey, prefetch_related() for ManyToMany
- Enable compression - Use
CompressionMiddleware for responses > 500 bytes
- Tune process count - Set
--processes to CPU core count for CPU-bound workloads
Security
- Always validate input - Use msgspec structs for request body validation
- Use HTTPS in production - Terminate TLS at nginx/load balancer
- Configure BOLT_TRUSTED_PROXIES - Required behind reverse proxies for accurate rate limiting
- Rotate JWT secrets - Implement key rotation for long-lived deployments
Maintainability
- Organize by feature - Group related endpoints in feature modules
- Use dependency injection - Extract reusable logic with
Depends()
- Write tests - Use
AsyncAPITestClient for integration tests
- Document APIs - Leverage auto-generated OpenAPI docs at
/docs
Command-Line Reference
# Development server with native Rust reloader
python manage.py runbolt --dev
# Production with custom settings
python manage.py runbolt --host 0.0.0.0 --port 8000 --processes 4
# Worker recycling
python manage.py runbolt --max-rss 512000 --workers-lifetime 3600
# Increase socket backlog
python manage.py runbolt --processes 4 --backlog 2048
# Adjust keep-alive timeout
python manage.py runbolt --processes 4 --keep-alive 30
# Skip startup checks
python manage.py runbolt --skip-checks
Version Migration Gates
| Version | Breaking Change | Before / After |
|---|
| v0.4.0 | Python 3.12+ required | Requires upgrading runtime; no code changes |
| v0.6.0 | SessionAuthentication removed | Before:<br>from django_bolt.auth import SessionAuthentication<br>api = BoltAPI(auth=[SessionAuthentication()])<br><br>After:<br>Use JWTAuthentication or APIKeyBearer |
| v0.10.0 | Cookie-JWT CSRF check ON by default | Before:<br>JWTAuthentication(cookie=True)<br>(no CSRF check for API clients)<br><br>After:<br>JWTAuthentication(cookie=False)<br>for non-browser clients |
| v0.10.3 | BOLT_TRUSTED_PROXIES required behind proxies | Before:<br>(rate limiting shared across all callers behind proxy)<br><br>After:<br>BOLT_TRUSTED_PROXIES = ["10.0.0.0/8", "192.168.0.0/16"]<br>in settings.py |
| v0.11.0 | Nested() removed — use plain type hints | Before:<br>author: Nested(AuthorSerializer)<br><br>After:<br>author: AuthorSerializer |
References
Deep Dives
The following reference files contain detailed coverage of advanced topics. Load them on demand when working on specific features:
- auth-security.md — Authentication, Permissions & Guards, Middleware, Rate Limiting, Error Handling
- responses-serialization.md — Responses, msgspec Serializers, Pagination, Request Object, Dependency Injection
- websockets-uploads.md — WebSockets, File Uploads
- orm-background.md — Django ORM Patterns, Background Tasks
- deployment.md — Deployment, MCP Servers
- drf-migration.md — Comparison with DRF/Django Ninja, Migration from DRF