Sitemap
Tech Tonic

Articles on popular things like Node.js, Deno, Bun, etc.

Node.js: The fastest web framework in 2025

6 min readJun 8, 2025

--

Introduction

This article serves as the 2025 update to one of my most widely read pieces over the past three years. At the start of 2024, I published a comparative analysis of the most popular web frameworks in the Node.js ecosystem. That article continues to attract significant traffic and remains the top result on Google for its topic.

However, given the rapid pace of change in the web development landscape, that piece, now over 18 months old, no longer reflects the current state of the ecosystem. As we approach mid-2025, it’s time for a comprehensive refresh.

Coincidentally, TechEmpower has also released Round 23 of their widely respected benchmarking series:

TechEmpower Web Framework Performance Comparison
Performance comparison of a wide spectrum of web application frameworks and platforms using community-contributed tests.
www.techempower.com

This new series of articles builds upon TechEmpower’s findings, with a specific focus on resource usage and latency in addition to raw requests per second (RPS).

The series will progress through multiple layers of real-world web application complexity — from the classic “Hello, world” example to handling JSON data, serving static files, performing database reads, and ultimately implementing practical use cases such as a URL shortener, a QR code generator, and JWT-based MySQL record access.

In this first installment, we begin with the foundational “Hello, world” example. While not practical on its own, it serves as a consistent and neutral baseline for all subsequent benchmarks.

Let’s dive in.

Before you read further, the other articles in this series are:

Testing Environment

All tests are executed on a MacBook Pro M2 with 16G RAM. There are 8+4 CPU cores. The load tester is Bombardier (written in Go). The Node.js version is v23.11.1 (latest stable release at the time of writing).

I’m including Node’s native server too. This gives a good baseline for all the other frameworks. It is well known that Node’s native server is pretty much a bare-bones server.

The code for each application is as follows:

Native

import http from "node:http";

const reqHandler = (req, resp) => {
try {
if (req.method !== "GET") {
return resp.writeHead(405).end();
}
if (req.url !== "/") {
return resp.writeHead(404).end();
}
resp.writeHead(200, {
"content-type": "text/plain",
});
resp.end("Hello world");
} catch (e) {
resp.writeHead(500).end();
}
};

http.createServer(reqHandler).listen(3000);

Note: Native code is longer so that it is close to the other frameworks where we usually don’t see this code.

Express

(Etag is disabled because other frameworks doesn’t send it by default)

import express from "express";

const app = express();

const reqHandler = (req, res) => {
res.send("Hello World!");
};

app.disable("etag");
app.get("/", reqHandler);

app.listen(3000, () => console.log("Listening on 3000"));

Fastify

import Fastify from "fastify";

const fastify = Fastify({
logger: false,
});

const reqHandler = (request, reply) => {
reply.send("Hello World!");
};

fastify.get("/", reqHandler);

fastify.listen({ port: 3000 });

Koa

import Koa from "koa";
import Router from "@koa/router";

const app = new Koa();
const router = new Router();

const reqHandler = (ctx, next) => {
ctx.body = "Hello World!";
};

router.get("/", reqHandler);

app
.use(router.routes())
.use(router.allowedMethods());

app.listen(3000);

Hapi

import Hapi from "@hapi/hapi";
const server = Hapi.server({
port: 3000,
host: "127.0.0.1",
});

const reqHandler = (request, h) => {
return "Hello World!";
};

server.route({
method: "GET",
path: "/",
handler: reqHandler,
});

server.start();

Restify

import restify from "restify";

const reqHandler = (req, res, next) => {
res.contentType = "text/plain";
res.send("Hello World!");
next();
};

var server = restify.createServer();
server.get("/", reqHandler);
server.listen(3000);

Hyper-Express

import HyperExpress from "hyper-express";
const webserver = new HyperExpress.Server();

const reqHandler = (request, response) => {
response.send("Hello World!");
};

webserver.get("/", reqHandler);
webserver.listen(3000);

Ultimate-Express

New entrant this time.

import express from "ultimate-express";

const app = express();

const reqHandler = (req, res) => {
res.send("Hello World!");
};

app.get("/", reqHandler);

app.listen(3000, () => console.log("Listening on 3000"));

NestJS

(The NestJS app code is generated through their CLI. Only few relevant files are shown here)

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.getHttpAdapter().getInstance().set('etag', false);
await app.listen(3000);
}
bootstrap();

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
getHello(): string {
return this.appService.getHello();
}
}

app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}

NestJS (Fastify)

By default, NestJS uses express adapter. Therefore, the performance is slightly worse than Express (which itself is the slowest in this lot). Fortunately, NestJS allows configuring fastify adapter instead of express.

main.ts

import { NestFactory } from "@nestjs/core";
import {
FastifyAdapter,
NestFastifyApplication,
} from "@nestjs/platform-fastify";
import { AppModule } from "./app.module";

async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);

await app.listen(3000);
}
bootstrap();

All other files are same as NestJS (express).

Results

A total of 1M requests are executed for 100 concurrent connections. Along with latencies, I’ve also collected CPU and memory usage. This gives a complete picture. Just having high RPS is not enough. We also need to know the cost of the RPS.

The results in chart form are as follows:

Verdict

Before diving into the analysis, it’s important to acknowledge that this comparison includes a diverse mix of frameworks — ranging from lightweight libraries to feature-rich, opinionated platforms. Among them, NestJS (available with both Express and Fastify underpinnings) stands out as a full-fledged application framework, offering significantly more built-in functionality than most others in this list. As such, comparing NestJS directly with minimalist frameworks like Koa may not be entirely equitable.

In terms of raw performance, Express emerges as the slowest, handling approximately 20,000 requests per second. However, it’s worth emphasizing that Express has stood the test of time. It remains the most mature, reliable, and widely adopted web framework in the Node.js ecosystem. Despite lagging in raw throughput, Express continues to be a dominant force and is unlikely to lose its relevance any time soon.

At the other end of the spectrum, Hyper-Express retains its position as the fastest option, consistent with the findings from the 2024 analysis. Close behind is Ultimate-Express, which also demonstrates excellent performance. Both leverage uWebSockets under the hood, enabling similar high-throughput characteristics.

As for my personal recommendation, I lean toward Fastify (or Nest-Fastify) as a balanced choice. It consistently delivers solid performance — approximately 87,000 requests per second — while maintaining the reliability and robustness expected from a production-ready framework.

Winner: HyperExpress

This was only about the do-nothing ‘hello world’ server. The winning margins might not be as wide for more complex cases. I’ll get to them very soon.

Once again, the other articles in this series are:

--

--

Tech Tonic
Tech Tonic

Published in Tech Tonic

Articles on popular things like Node.js, Deno, Bun, etc.

Responses (2)