Benchmarks
NestRS is measured against idiomatic NestJS 11 on both adapters — Fastify (its best case) and Express (its default) — serving a byte-identical HTTP contract at the same stack depth. The harness ships in the repository; one command reproduces the table below on your hardware.
git clone https://github.com/YV17labs/NestRS && cd NestRS/benchjust benchResults
Section titled “Results”Tier T1 — a JSON response through the framework’s router, DI container, an injected service, and its standard serialization path. Each server pinned to one core (the regime that favours Node: an event loop loses nothing to pinning), load generator on separate cores, medians of repeated timed runs.
| 1 pinned core | NestRS | NestJS · Fastify | NestJS · Express | vs best |
|---|---|---|---|---|
| Throughput | ~241k req/s | ~97k req/s | ~58k req/s | ×2.5 |
| Latency p50 | 0.24 ms | 0.62 ms | 1.06 ms | ×2.6 |
| Latency p99 | 0.42 ms | 1.27 ms | 2.11 ms | ×3.0 |
| Memory under load | 8 MB | 198 MB | 206 MB | ×25 |
| Memory idle | 6 MB | 86 MB | 85 MB | ×14 |
| Cold start → first 200 | ~15 ms | ~360 ms | ~330 ms | ×23 |
The comparison stays per-core on purpose. Scaling out, Node clusters — one ~200 MB process per core — while one NestRS binary takes every core in a single process a few MB large: the per-core ratio is what each added vCPU buys you, on either side. The static tier (T0, no service call) tells the same story as T1 on both sides, so what you see is framework overhead, not serialization cost.
Measured 2026-07-25 on a 4-core aarch64 Linux host — rustc 1.96.1, Node
24.18, @nestjs/core 11.1.28, oha 1.15; 64
connections, discarded warmup, medians of 3 × 15 s runs; cold start is the
median of repeated boots, the one figure the harness samples once per run.
Absolute figures
move with hardware; the ratios are the claim — every run embeds its
machine fingerprint, and the one command above replays the full protocol on
yours.
The contract
Section titled “The contract”Every contestant serves the exact same surface, verified before anything is measured:
- Same bytes. Status, media type, and body compared byte for byte against golden files by a conformance gate. A contestant that fails the gate is never benchmarked.
- Same depth. The measured route must traverse router → DI-built controller → injected service → the framework’s standard JSON path. Inlining the response, bypassing DI, or hand-rolling serialization disqualifies the contestant — enforced by review, and the sources are deliberately small enough to read.
- Same posture. Each side is written the way its own CLI scaffolds it —
release build vs
NODE_ENV=production, framework defaults untouched, no per-request telemetry on either side, no reverse proxy, no TLS.
The protocol
Section titled “The protocol”- Server and load generator pinned to disjoint CPU sets — they never share a core.
- Per tier: a discarded warmup, then repeated timed runs; medians reported, raw output kept.
- Headline figures use the single-core regime — Node’s best case. The all-cores regime is reported separately, where tokio’s multi-threaded runtime pulls further ahead.
- Every result embeds its fingerprint: CPU, kernel, toolchains, resolved dependency versions, and the exact protocol parameters.
Limits
Section titled “Limits”- The harness compares framework overhead on a hello-world contract. It does not claim your domain logic gets 1.5× faster — it claims the floor under it is lower and the ceiling higher.
- Neither side ships per-request telemetry — measuring “telemetry on” vs “telemetry absent” would compare features, not frameworks.
- Adding a contestant is a directory with a manifest — the harness knows no
framework. The contract, the gate, and the how-to live in
bench/.
Going further
Section titled “Going further”- Why NestRS — what the framework carries beyond the numbers.
- Why not axum? — same native core; the comparison that actually matters.
- Getting started — scaffold the same hello and measure it yourself.
Built by YV17labs