Benchmarks
@dunx/http against raw Bun.serve, Elysia, Hono, Fastify, Express and bare node:http. The number worth having is the gap to raw Bun.serve: dunx is a layer on that exact API, so the gap is dunx's own overhead. Rows are ordered by measured throughput, losses included, and request logging gets its own row rather than being folded into the framework's number.
AMD Ryzen 9 5950X 16-Core Processor, 32 logical cores, 62.7 GiB RAM · linux 7.0.0-31-generic x64 · bun 1.4.2 · node v24.21.0
64 connections · 3s warmup · 5 x 5s measured (median) · 7 startup samples · oha 1.15.0
dunx vs raw Bun.serve
92.1-99.7%
of the ceiling, across 5 scenarios. The rest is framework overhead.
Against the fastest rival
0W 3T 2L
Won 0, tied 3, lost 2 of 5. A gap inside ±3 points is a tie on this setup, not a result.
Cold start
1.93x
46.2 ms against Bun.serve (raw)'s 24.0 ms - rank 6 of 20. dunx's clearest loss on this page.
Scenario by scenario
| Scenario | dunx | Fastest rival | Rank | Verdict |
|---|---|---|---|---|
| Plain text | 99.6% | Elysia 99.0% | 2 / 20 | tied |
| JSON | 95.6% | Axum (Rust) 97.8% | 3 / 20 | tied |
| Path parameter | 94.4% | Elysia 99.6% | 4 / 20 | behind |
| Body validation | 92.1% | Axum (Rust) 93.7% | 3 / 20 | tied |
| Cache and database | 99.7% | Axum (Rust) 127.1% | 4 / 20 | behind |
Bun
Node
Go
Rust
JVM
.NET
Python
@dunx/http
Plain text
GET /plaintext
GET returning a fixed text/plain body. Measures raw request/response dispatch.
JSON
GET /json
GET returning a small object serialised to JSON. Adds serialisation cost.
Path parameter
GET /params/42
GET with one path parameter echoed back as JSON. Adds route-matching cost.
Body validation
POST /validate
POST with a JSON body parsed and validated against the same zod schema in every subject, echoed back.
Cache and database
GET /io
GET reading one key from Redis and then one indexed row from Postgres with a bound parameter, merged into one JSON object.
Cold start
Process spawn to first served request, median of 7 samples. Polled at about 1 ms, so treat anything under 5 ms as a tie.
Memory
Resident set of the whole process tree, read from /proc at 20 Hz inside each measured window. Boot is taken after the first served request and before any load; peak is the highest sample under it. Runs are seconds long, so neither says anything about heap growth at hour six.
Stability
Throughput answers how fast one request is. This answers whether the process survives the night. bun run soak drives 28 weighted operations against the full example - HTTP, websocket churn, SQLite, Redis, S3, the queue, the outbound client, guards, auth, validation failures and rate limiting - and fits the settled heap across rounds rather than the in-flight series, which measures the allocator instead of a leak.
3,668,137
calls in 365s
10,051/s
sustained
0
failures
25.1 to 25.0 MiB
settled heap, 12 rounds
The same harness runs in CI at 40 seconds and concurrency 12, which is too short for a leak verdict and long enough to catch an unexpected status, a stalled loop, a subscriber that outlived its socket, and a shutdown that hangs.