Can Rust Improve Real Django API Performance? Testing Django-Bolt Beyond Synthetic Benchmarks
TL&DR - Key Takeaways
- Django-Bolt reports very high throughput for simple JSON endpoints by moving more HTTP request handling into Rust while keeping the existing Django application and Python logic in place.
- Synthetic benchmarks do not show the whole picture: Database access, authentication, application logic and serialization can significantly reduce the advantage seen on lightweight endpoints.
- Teams should benchmark representative production endpoints and track latency, CPU and database behavior alongside requests per second to determine where Django-Bolt actually delivers value.
For teams already using Django, Django-Bolt offers a way to improve API performance without switching to another framework. It keeps Django in place but uses Rust to handle incoming web requests. In its current published benchmarks, Django-Bolt reports 311,000 requests per second for a simple JSON endpoint. The project is careful about that number, though. It notes that benchmark results depend on the hardware and are better used for comparison than as a prediction of what an application will do in production.
The question I find more useful is what happens after the request stops being simple. A real Django API may need to check a user's identity, retrieve records from a database, run application logic and prepare the response. Each of those steps adds time that the simple benchmark never had to account for. There is little doubt from the headline benchmark that Django-Bolt can handle a simple request quickly. What I would want to know before considering it for an existing application is how much of that advantage remains once the API starts doing the work it was built to do.
Understanding What Django-Bolt Actually Changes
Most teams running a mature Django application are not looking for an excuse to replace it. The models work, the authentication is already wired in and years of application logic may depend on Django. Things get more difficult when the API needs to handle more traffic, and serving those requests begins to get in the way.
Django-Bolt changes this by moving more of the HTTP work into Rust. Actix Web powers the HTTP server, while PyO3 provides the connection back to Python when the request needs Django's ORM, authentication or application code.
What I find more useful here than the choice of Rust itself is how contained that change can be. A team can test a different serving layer without first turning the exercise into a rewrite of the Django application. For a codebase with years of models, permissions, middleware and integrations behind it, that is a much smaller change than moving the API to another framework.
Once Actix Web hands the request to Python, Django still has the endpoint's real work to do. The response may depend on a database query, application code or another service that answers first. Whatever happens there still adds to the time the user waits.
Why Synthetic Benchmarks Often Tell an Incomplete Story
A static JSON endpoint doesn't ask much of the application. The request comes in, a simple response goes back out, and there is still very little in between. That makes it easier to see the speed of Django-Bolt's Rust-powered server. There is nothing wrong with testing it this way. If you want to know how much overhead the framework adds, keeping the endpoint simple makes sense.
I become more cautious when that requests-per-second number is used to suggest what an existing API might handle. The endpoints people actually depend on are usually doing more. They may authenticate a user, query the database, check permissions and prepare a response before anything goes back to the client. So, I wouldn't throw out the lightweight benchmark. I would just be careful about what I use it to prove.
That way, it can tell us how quickly Django-Bolt handles a request when there is very little application work in the way. It cannot tell us how much faster an endpoint will become when most of its time is already being spent inside Django or waiting for data. For a team testing Django-Bolt, I would take one of the endpoints they actually care about and benchmark that next. Keep the database calls, authentication and application work that normally happen there. A lower number from that test may be far more useful than an impressive result from an empty endpoint, because it tells you whether Django-Bolt is speeding up a part of the application that was actually holding it back.
Designing a Fair Performance Test
The test should show what happens to Django-Bolt's lead as the endpoint takes on the work a real API has to do. Putting database access, authentication, application logic and serialization into the first test makes that harder to see. Begin with a small endpoint that keeps most of that work out of the measurement:
@api.get("/json-1k")
async def json_1k():
return JSON_1K
From there, add database access while leaving the hardware, concurrency and the rest of the test setup unchanged:
@api.get("/users", response_model=list[UserSchema])
async def users():
return [user async for user in User.objects.all()[:10]]
That's the gap I'd focus on. If Django-Bolt has a large lead on the first endpoint and a smaller one after the ORM is involved, the database work has already changed the result. Authentication, larger responses and additional application work can then be added separately. Doing this separately makes it much easier to see where Django-Bolt's advantage changes and what caused it.
Requests per second should not be the only number on the screen either. I would keep p50, p95 and p99 latency beside it and watch CPU and database activity as the load increases. A server can finish more requests overall while some requests are quietly taking much longer. If that only appears at p99, an RPS chart will miss it.
Faster request handling can also change what happens at the database. If Django-Bolt sends more requests through the application in the same amount of time, more queries can arrive at the database within that window as well. The API may report higher throughput while query waits begin to climb. In that case, Django-Bolt hasn't slowed the database; it has exposed a limit that the slower request path was helping to hide.
By the end of the test, I want to know where Django-Bolt actually saved time, how much of that saving remained once the database and application code got involved and what started slowing down next. That tells a team much more than the final RPS number. It shows where Django-Bolt genuinely helps their application and where they may need to look next.
Benchmark Results: Where Django-Bolt Wins and Where It Doesn't
Django-Bolt's biggest gains are easier to see on endpoints that don't spend much time waiting on anything else. Add database work and that advantage has something else to compete with. An async ORM endpoint fetching 10 rows from SQLite reaches roughly 21,000-27,000 requests per second. I wouldn't carry that number over to an application running PostgreSQL or MySQL.
What caught my attention was what happened once fetching data became part of the request. Django-Bolt could still handle the request quickly, but it couldn't do much about the time spent waiting for the database. If an endpoint is already slow because most of its time is spent on a query, speeding up everything before that query won't change much. Django-Bolt can get the request there sooner, but it can't make that query return sooner. If the database wait grows under load, the time saved before the query matters even less.
A faster server may also put more pressure on the database. If Django-Bolt handles more requests in the same amount of time, the database may have to handle more queries too. That can show up as longer query wait times as load increases. The server can look much faster while the endpoint itself improves by far less.
Before reading too much into the benchmark, look at the endpoints you're trying to fix. If they're slow before they reach the database, Django-Bolt could help. If most of the delay comes from the queries themselves, getting to those queries faster won't solve much. You'd be speeding up the wrong part of the request.
Does Faster Serialization Improve Real API Performance?
Django-Bolt uses msgspec for serialization, and the performance numbers help explain why. Its benchmarks report roughly 10-85x faster encoding and decoding for supported types than several alternative Python libraries. These results are impressive, but they come from testing serialization on its own. An API has plenty of other work to do before a response reaches the client.
A simple schema looks like this:
import msgspec
class User(msgspec.Struct):
id: int
username: str
email: str
Where msgspec starts to earn its place is on endpoints that require a lot of data preparation. Returning a small object doesn't leave much serialization work to speed up. Return hundreds or thousands of objects, especially on a busy endpoint, and the time spent turning that data into a response can become much harder to ignore.
Serialization benchmarks can make the gain look much larger than what eventually shows up in the API. A faster serializer helps, but only with the time the endpoint was spending on serialization to begin with. If that was already a small part of the request, there simply isn't much time to win back. Larger responses give msgspec more work to speed up, and that is where the difference is more likely to show.
Performance Is Only One Deployment Decision
A strong benchmark result doesn't tell you whether Django-Bolt is worth running in production. Someone has to deploy it, debug it and keep it working after the performance test is over.
Django-Bolt adds Rust to an application the team may have spent years running as Python. The Django application may look much the same, but troubleshooting won't always stop there. If a request fails before it reaches the Python code, the team may have to look beyond the part of the application they know best. Engineers who already work with Rust will find that unusual. For a Python-only team, it is extra ground to cover when production is already having a bad day.
Django-Bolt is still listed as Alpha on PyPI. That wouldn't stop me from testing it, but it is something I'd keep in mind before using it in production. Django REST Framework and FastAPI have been around much longer, so more help is available when something goes wrong. With Django-Bolt, the team may have to figure out more things on its own. That extra work only makes sense if Django-Bolt makes the application fast enough to be worth it.
The cost of running the stack still has to make sense given the performance you get. If the serving layer is already holding back the application, the improvement may well justify the extra work. But if most of the slowdown is happening elsewhere and Django-Bolt barely changes the result, taking on more to run and maintain starts to make a lot less sense.
Final Thoughts
Django-Bolt shows that a Django application can achieve faster serving without abandoning the framework it was built on. The benchmark numbers are impressive, and some of that speed can carry into real APIs. How much carries over depends on where the application is already spending its time. When request handling is a source of slowdown, Django-Bolt can make a noticeable difference.
For teams considering Django-Bolt, it makes sense to test against the API they already run. The result that matters isn't the biggest number on the benchmark page. It's what happens to the endpoints they actually need to make faster.
Frequently Asked Questions
What does Django-Bolt change in a Django application?
Django-Bolt moves more HTTP request handling into Rust using Actix Web, while PyO3 connects requests back to Python when Django's ORM, authentication or application logic is needed.
Why might Django-Bolt's benchmark gains shrink on real APIs?
Real endpoints often spend significant time querying databases, checking permissions, authenticating users or running business logic. Django-Bolt can speed up request handling, but it cannot eliminate time spent waiting on those operations.
What should teams measure when evaluating Django-Bolt?
Alongside requests per second, teams should monitor p50, p95 and p99 latency, CPU usage and database activity. Higher throughput can expose downstream bottlenecks that a slower request layer previously hid.
Comments
No comments yet. Start the discussion.