Cross-posted from my blog. The original, with any updates, lives on fabwaseem.com.
"NestJS vs Hono vs Fastify" is one of the most common backend questions I see from TypeScript developers in 2026, and most answers are either opinion or benchmarks from 2023. So I ran my own: the same three routes on Express 5.3, Fastify 5.12, Hono 4.13, NestJS 12.3 on Express and NestJS 12.3 on Fastify, all on Node.js 26.13 (the current LTS), on the same machine, with the same load generator.
This post gives you the numbers, the exact code, and a straight answer on which one to pick for an API, a SaaS backend or an edge function.
The test setup
- Machine: one 8-vCPU Intel Xeon Linux VM. The server was pinned to one CPU core with
taskset -c 0(Node runs your JavaScript on one thread, so this is a fair per-process number). The load generator, autocannon 8, ran on 6 other cores. - Runtime: Node.js v26.13.0.
- Versions: express 5.3.0, fastify 5.12.8, hono 4.13.16 with @hono/node-server 2.1.6, @nestjs/core 12.3.3 with @nestjs/platform-express and @nestjs/platform-fastify 12.3.3. NestJS was compiled with TypeScript 5.9.3.
- Load: 100 connections, no pipelining, 3-second warm-up, then 8 seconds per route.
- Rounds: 3 full rounds, run one framework after another (interleaved), and I report the median of the 3.
- Routes:
-
GET /jsonreturns{"hello":"world"} -
GET /users/:idreturns the route parameter as JSON -
POST /echoparses a small JSON body and sends it back
-
No logging, no validation, no database. This measures framework overhead only. Every request returned 2xx.
The code
Express 5:
import express from 'express'; const app = express(); app.use(express.json()); app.get('/json', (req, res) => res.json({ hello: 'world' })); app.get('/users/:id', (req, res) => res.json({ id: req.params.id })); app.post('/echo', (req, res) => res.json(req.body)); app.listen(3000); Fastify 5:
import Fastify from 'fastify'; const app = Fastify({ logger: false }); app.get('/json', async () => ({ hello: 'world' })); app.get('/users/:id', async (req) => ({ id: req.params.id })); app.post('/echo', async (req) => req.body); await app.listen({ port: 3000 }); Hono 4 on Node.js:
import { Hono } from 'hono'; import { serve } from '@hono/node-server'; const app = new Hono(); app.get('/json', (c) => c.json({ hello: 'world' })); app.get('/users/:id', (c) => c.json({ id: c.req.param('id') })); app.post('/echo', async (c) => c.json(await c.req.json())); serve({ fetch: app.fetch, port: 3000 }); NestJS 12 (one file, the adapter is picked by an env variable):
import 'reflect-metadata'; import { Body, Controller, Get, Module, Param, Post } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { FastifyAdapter } from '@nestjs/platform-fastify'; @Controller() class AppController { @Get('json') json() { return { hello: 'world' }; } @Get('users/:id') user(@Param('id') id: string) { return { id }; } @Post('echo') echo(@Body() body: unknown) { return body; } } @Module({ controllers: [AppController] }) class AppModule {} async function bootstrap() { const app = process.env.ADAPTER === 'fastify' ? await NestFactory.create(AppModule, new FastifyAdapter(), { logger: false }) : await NestFactory.create(AppModule, { logger: false }); await app.listen(3000); } bootstrap(); Results: requests per second (one core, higher is better)
| Framework | GET /json | GET /users/:id | POST /echo |
|---|---|---|---|
| Fastify 5.12 | 85,448 | 82,040 | 59,746 |
| Hono 4.13 (Node) | 83,184 | 80,728 | 51,192 |
| NestJS 12 + Fastify | 70,082 | 69,010 | 48,596 |
| Express 5.3 | 48,564 | 45,476 | 32,969 |
| NestJS 12 + Express | 43,844 | 39,340 | 29,563 |
Median of 3 rounds. Latency was low for all of them at this load: p50 was 1-3 ms and p99 was 2-11 ms. The highest p99 (11 ms) was NestJS on Express for POST /echo.
Results: startup time and memory
| Framework | Start to first 200 | RSS idle | RSS after load |
|---|---|---|---|
| Hono 4.13 | 90 ms | 65.6 MB | 110.5 MB |
| Express 5.3 | 115 ms | 66.9 MB | 115.7 MB |
| Fastify 5.12 | 158 ms | 73.8 MB | 116.5 MB |
| NestJS 12 + Fastify | 404 ms | 104.9 MB | 136.2 MB |
| NestJS 12 + Express | 486 ms | 105.2 MB | 149.9 MB |
"Start" is the time from spawning the process to the first successful response, polled every 5 ms.
What the numbers say
1. Fastify and Hono are close; Express is roughly half as fast. On simple GET routes Fastify and Hono were within 3% of each other. Both did about 1.7-1.8x the requests of Express 5. Express 5 is a big cleanup (async errors, better path matching), but it did not change the performance picture.
2. Fastify wins on JSON bodies. On POST /echo, Fastify did 17% more requests than Hono (59,746 vs 51,192). Fastify has its own body parser and a compiled serializer path. Hono on Node goes through the Web Request adapter in @hono/node-server, and c.req.json() pays for that.
3. NestJS costs you about 16-19% on Fastify, and the adapter matters more than the framework. NestJS on Fastify kept 81-84% of raw Fastify's throughput on every route. NestJS on Express kept 87-90% of raw Express. But NestJS + Fastify was 60-75% faster than NestJS + Express on GET routes. If you already run NestJS, switching the adapter is the cheapest performance win you have.
4. NestJS starts 3-5x slower and uses ~40 MB more memory. That's the DI container, the module scan and reflect-metadata. It does not matter for a long-running API. It does matter for serverless cold starts and for scale-to-zero containers.
5. Hono starts fastest and uses the least memory. 90 ms and 65.6 MB idle. Add the fact that the same Hono app runs on Cloudflare Workers, Bun, Deno and Vercel, and it is the best default for small services and edge functions.
Caveats (read these before quoting the numbers)
- This is framework overhead with trivial handlers. A real handler that hits Postgres for 2-5 ms will flatten these differences. At 30,000+ requests per second per core, the framework is rarely your bottleneck.
- One VM, one core per server, 100 connections. Your hardware will give different absolute numbers; the ranking is what to take away.
- Express used
express.json(); Fastify and Nest used their default parsers. No schemas, so Fastify's schema-based serialization was not used. Fastify can serialize faster with a response schema; I did not test that here. - Hono was tested on Node.js through
@hono/node-server. Hono on Bun or Workers is a different test.
Which one should you choose?
| If you are building... | Pick | Why |
|---|---|---|
| A large team API with modules, DI, guards, OpenAPI | NestJS on the Fastify adapter | Structure and conventions; the Fastify adapter kept 81-84% of raw Fastify speed here |
| A high-throughput Node.js JSON API | Fastify | Fastest here, especially on POST bodies; mature plugin ecosystem |
| Edge functions, Workers, multi-runtime code, small services | Hono | Near-Fastify speed on Node, fastest startup, smallest memory, runs everywhere |
| An existing Express app | Stay on Express 5 unless you have a measured performance problem | Migration cost is real; the gap only matters at high load |
My own default: Hono for small services and anything that might run on the edge, NestJS with Fastify when a SaaS backend needs structure and several developers. I covered the Express side in more depth in Hono vs Express, and what changed in NestJS itself in NestJS 12.
How to reproduce
Install the same versions, start each server pinned to one core, and run autocannon:
npm i hono@4.13.16 @hono/node-server@2.1.6 fastify@5.12.8 express@5.3.0 \ @nestjs/core@12.3.3 @nestjs/common@12.3.3 @nestjs/platform-express@12.3.3 \ @nestjs/platform-fastify@12.3.3 reflect-metadata rxjs autocannon@8 taskset -c 0 node fastify.mjs & npx autocannon -c 100 -d 8 http://127.0.0.1:3000/json npx autocannon -c 100 -d 8 -m POST -H content-type=application/json \ -b '{"name":"Waseem","tags":["a","b","c"],"n":7}' http://127.0.0.1:3000/echo Run each framework a few times, alternate the order, and use the median.
FAQ
Is Hono faster than Fastify?
Not on Node.js in my test. On simple GET routes they were within 3% (Fastify 85,448 vs Hono 83,184 requests per second on one core). On a JSON POST body Fastify was 17% faster. Hono started faster (90 ms vs 158 ms) and used less memory.
Is NestJS slower than Express?
NestJS adds overhead on top of whatever adapter it uses. NestJS on Express did about 10% fewer requests than plain Express on GET /json. NestJS on the Fastify adapter was faster than plain Express by about 44% on the same route.
Should I use the Fastify adapter in NestJS?
If you don't depend on Express-only middleware, yes. In this benchmark NestJS on Fastify served 60-75% more GET requests per second than NestJS on Express, started faster (404 ms vs 486 ms) and used less memory under load.
Is Express 5 faster than Express 4?
I did not benchmark Express 4 in this test. Express 5 focuses on modern behaviour (promise-aware error handling, updated path matching) rather than speed, and in this test it was still about 43% behind Fastify on GET routes.
Which is best for a SaaS backend in 2026?
For a team project with auth, modules and many endpoints, NestJS on the Fastify adapter gives you structure without giving up much speed. For a small API or one that needs to run on Cloudflare Workers or Bun too, Hono is the simpler choice.
Originally published on fabwaseem.com.
