Benchmarks
Transparent, reproducible benchmarks — framework comparisons and deep performance profiling.
Last updated: 2026-06-08
All figures come from one lab setup (CPU, OS, Node, tool versions). Your hardware will give different absolute RPS, but relative rankings stay consistent. Use the same scripts for your own numbers. In production, databases and external calls usually dominate latency — not the framework choice.
How to read this page
Published RPS are snapshots, not guarantees. Run apps/benchmark locally when you need numbers
for capacity planning.
Overview
The benchmark suite in apps/benchmark supports 2 tools that produce slightly different absolute numbers but the same relative rankings:
wrk
Primary — C-based
Runs as a separate C process, isolated from Node.js. No event loop interference. Install via `apt install wrk`. Used for deep profiling.
autocannon
Fallback — Node.js
Runs in-process. Always available via pnpm. Shares the event loop with the server — numbers may differ slightly from wrk. Used for quick comparisons.
The runner auto-detects wrk first; use --tool wrk|autocannon to force a specific tool.
Tool Comparison: NextRush v3
How the same test scenarios differ between wrk and autocannon on identical hardware (64 connections, 10s):
| Scenario | wrk RPS | autocannon RPS | Variance |
|---|---|---|---|
| Hello World | 31,311 | 31,733 | +1.3% |
| Route Parameters | 29,688 | 29,534 | -0.5% |
| POST JSON | 18,460 | 19,192 | +4.0% |
| Middleware Stack | 32,377 | 32,220 | -0.5% |
The numbers are within ~4% across all scenarios, so relative framework rankings stay consistent regardless of which tool you use.
Framework Comparison (autocannon)
Head-to-head comparison against popular Node.js frameworks. All servers implement identical endpoints with equivalent work.
Test Environment
| Property | Value |
|---|---|
| Node.js | v25.9.0 |
| Platform | Linux (x64) |
| CPU | Intel Core i5-8300H @ 2.30GHz |
| Cores | 8 |
| Memory | 15.5 GB |
| Framework | Version |
|---|---|
| Raw Node.js | built-in (zero-framework baseline) |
| NextRush v3 | 3.0.x |
| Fastify | 5.x |
| Hono | 4.x (via @hono/node-server) |
| Koa | 3.x (with koa-router) |
| Express | 5.x |
| Setting | Value |
|---|---|
| Tool | autocannon v8 |
| Duration | 10 seconds per test |
| Connections | 64 concurrent |
| Pipelining | 1 (no pipelining — realistic) |
| Profile | quick (single run per scenario) |
Detailed Scenarios
Baseline — a JSON response with no routing or body parsing.
app.get('/', (ctx) => ctx.json({ message: 'Hello World' }));Hello World Throughput
1ms p50, 3ms p99
1ms p50, 3ms p99
1ms p50, 3ms p99
2ms p50, 4ms p99
2ms p50, 5ms p99
3ms p50, 6ms p99
| Framework | RPS | Latency p50 | Latency p99 |
|---|---|---|---|
| Raw Node.js | 36,903 | 1ms | 3ms |
| Fastify | 34,063 | 1ms | 3ms |
| NextRush v3 | 31,733 | 1ms | 3ms |
| Hono | 28,209 | 2ms | 4ms |
| Koa | 23,845 | 2ms | 5ms |
| Express | 19,496 | 3ms | 6ms |
Dynamic route — measures router performance.
app.get('/users/:id', (ctx) => ctx.json({ id: ctx.params.id }));Route Parameter Throughput
1ms p50, 3ms p99
2ms p50, 3ms p99
2ms p50, 4ms p99
2ms p50, 4ms p99
2ms p50, 5ms p99
3ms p50, 6ms p99
| Framework | RPS | Latency p50 | Latency p99 |
|---|---|---|---|
| Raw Node.js | 33,936 | 1ms | 3ms |
| Fastify | 31,095 | 2ms | 3ms |
| NextRush v3 | 29,534 | 2ms | 4ms |
| Hono | 25,966 | 2ms | 4ms |
| Koa | 22,421 | 2ms | 5ms |
| Express | 18,209 | 3ms | 6ms |
POST with JSON body — body parser overhead.
app.post('/users', (ctx) => ctx.json(ctx.body, 201));POST JSON Throughput
2ms p50, 4ms p99
3ms p50, 6ms p99
3ms p50, 6ms p99
4ms p50, 7ms p99
4ms p50, 9ms p99
5ms p50, 11ms p99
| Framework | RPS | Latency p50 | Latency p99 |
|---|---|---|---|
| Raw Node.js | 24,936 | 2ms | 4ms |
| NextRush v3 | 19,192 | 3ms | 6ms |
| Fastify | 18,532 | 3ms | 6ms |
| Koa | 15,323 | 4ms | 7ms |
| Express | 13,063 | 4ms | 9ms |
| Hono | 10,798 | 5ms | 11ms |
NextRush stays within 4% of Fastify here — body parsing is a common bottleneck, so the gap narrows when frameworks do real work.
5-layer middleware stack (timing, logging, auth check, CORS, body parsing) — best reflects real-world app overhead.
Middleware Stack Throughput
1ms p50, 3ms p99
1ms p50, 3ms p99
2ms p50, 4ms p99
2ms p50, 5ms p99
2ms p50, 5ms p99
3ms p50, 7ms p99
| Framework | RPS | Latency p50 | Latency p99 |
|---|---|---|---|
| NextRush v3 | 32,220 | 1ms | 3ms |
| Raw Node.js | 31,471 | 1ms | 3ms |
| Fastify | 28,744 | 2ms | 4ms |
| Hono | 22,258 | 2ms | 5ms |
| Koa | 21,125 | 2ms | 5ms |
| Express | 17,352 | 3ms | 7ms |
NextRush outpaces raw Node.js here — compose() pre-compiles the pipeline, so middleware dispatch costs near zero under concurrency.
Average RPS by Framework (autocannon)
Mean throughput across all 4 scenarios.
Baseline
-11.6% vs Raw Node.js
-11.5% vs Raw Node.js
-31.5% vs NextRush
-35.0% vs NextRush
-46.5% vs NextRush
Deep Profile (wrk)
NextRush v3 benchmarked with wrk — a C-based HTTP benchmark tool that runs as a separate process, isolated from Node.js. Unlike autocannon, wrk doesn't share the event loop with the server, which eliminates the "testing yourself with yourself" problem.
Methodology
| Setting | Value |
|---|---|
| Tool | wrk 4.2.0 |
| Duration | 10 seconds per test |
| Connections | 64 |
| Pipelining | Disabled (1 request at a time — realistic client) |
| Threads | 4 |
Scenario Breakdown
| Scenario | RPS | Latency p50 | Latency p99 | Memory (RSS peak) |
|---|---|---|---|---|
| Hello World | 31,311 | 1ms | 3ms | 109.8 MB |
| Route Parameters | 29,688 | 2ms | 4ms | 109.8 MB |
| POST JSON | 18,460 | 3ms | 6ms | 109.8 MB |
| Middleware Stack | 32,377 | 1ms | 3ms | 109.8 MB |
NextRush scales efficiently — the middleware stack pre-compilation means it can outperform even the hello-world baseline under concurrency.
Peak RPS by Scenario
wrk, 64 connections, 10s duration
1ms p50, 3ms p99 — fastest scenario
1ms p50, 3ms p99
2ms p50, 4ms p99 — radix tree overhead is minimal
3ms p50, 6ms p99 — body parsing cost
Memory Profile
RSS peak
109.8 MB
Highest observed process memory during wrk benchmark.
RSS average
103.3 MB
Average memory across the sampled wrk profile.
Samples
96
Memory samples collected while the profile was running.
Idle footprint
<200KB
Framework-level idle footprint outside the benchmark harness.
The numbers above include the full benchmark harness (wrk + server + monitoring). NextRush's own idle footprint is under 200KB.
Design Trade-offs: Fastify vs NextRush
Fastify edges ahead on raw throughput in these benchmarks. Here is what drives the gap:
| Area | Fastify | NextRush |
|---|---|---|
| JSON serialization | AOT with fast-json-stringify | Native JSON.stringify |
| Router | find-my-way (radix tree, mature) | Custom segment trie (newer) |
| Schema validation | Compiled JSON Schema | User-supplied transforms |
| HTTP parsing | Custom optimized | Standard Node.js parser |
Trade-off
Fastify tunes heavily for Node.js throughput. NextRush prioritizes zero dependencies, full TypeScript, and multi-runtime support. If single-platform HTTP throughput is your only concern, Fastify is the right choice. If you need portability and a lean core, NextRush wins on different terms.
Run Your Own Benchmarks
Quick comparison
cd apps/benchmark
pnpm install
pnpm bench:compare:quickDefaults to wrk if installed, otherwise falls back to autocannon.
Force a specific tool
# Use autocannon
node scripts/run.js --compare --tool autocannon
# Use wrk with full profile (60s, 4 concurrency levels, 5 runs)
node scripts/run.js --profile full --tool wrkResults are saved to results/ with timestamps. Each run generates a JSON file and a human-readable report.
How to use these numbers
Fastify edged ahead on mean RPS in this suite. NextRush came second. That ordering is useful for relative comparison on identical hardware — not as a universal ranking.
- Fastify tunes aggressively for Node.js throughput. Its serialization choices pay off in micro-benchmarks.
- In real apps, a single database call adds milliseconds per request. That gap between frameworks shrinks fast.
- NextRush keeps a dependency-free core, ships adapters for several runtimes, and supports optional DI and decorators. Different trade-offs, not a single winner.
Run your own benchmarks on your own hardware. That is the only number that matters.