Node.js Frameworks Worth Using in 2026 (and When to Skip Them)

In 2026, Fastify is my default for most Node APIs, Express still works fine for simple apps, NestJS pays off on larger teams that need structure, and Hono is great for small edge APIs. Skip frameworks entirely for tiny webhook or cron services.
Part of the complete web development guide.
On this page
- First, the question nobody asks: do you even need a framework?
- Express 5: still the safe answer
- Fastify: my default for new APIs
- NestJS: when your team needs a constitution
- Hono: the one that surprised me
- Honorable mentions and what I’d actually skip
- How I actually decide
- One thing that changed for everyone in 2026
- FAQ
- Is Express dead in 2026?
- Should I rewrite my Express app in Fastify?
- What about Bun and Deno? Do I still need Node frameworks?
Most “best framework” lists are written by people who have never shipped anything with the frameworks they’re ranking. This one isn’t. I’ve built and maintained Node services for years, mostly for small businesses and agencies around Long Island, and I’ve watched the ecosystem settle down in a way that makes the 2026 answer a lot less exciting and a lot more useful.
Short version: Express is still fine, Fastify is usually the better default, NestJS is worth it when your team is big enough to need rules, and Hono is the one genuinely new thing that changed how I write small APIs. Everything else is a niche tool that solves a specific problem well.
First, the question nobody asks: do you even need a framework?
Node has had a built-in HTTP server since forever. For a webhook receiver, a cron job that talks to three APIs, or an internal tool with six endpoints, you can skip frameworks entirely and be done before you’d finish reading NestJS docs.
import { createServer } from 'node:http';
const server = createServer((req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'content-type': 'application/json' });
return res.end(JSON.stringify({ ok: true }));
}
res.writeHead(404).end();
});
server.listen(3000);
That’s real production code for a lot of small services. The moment you need routing with parameters, body parsing, auth middleware, and error handling that doesn’t leak stack traces, you want a framework. That moment arrives fast, but it does arrive, and it’s worth knowing where your line is.
Express 5: still the safe answer
Express 5 finally shipped stable and it fixed the long-standing annoyance where an error thrown inside an async handler would hang the request instead of hitting your error middleware. That alone justifies upgrading if you’re still on 4.
import express from 'express';
const app = express();
app.use(express.json());
app.get('/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new Error('not found'); // now reaches the error handler
res.json(user);
});
app.use((err, req, res, next) => {
res.status(500).json({ error: 'Something went wrong' });
});
app.listen(3000);
Why people still pick it: the largest middleware ecosystem on the planet, every tutorial and Stack Overflow answer from the last decade targets it, and any developer you hire already knows it. Why people leave it: it’s slow compared to Fastify, it has no built-in schema validation, and its TypeScript support is bolted on rather than designed in.
If you’re maintaining an existing Express app, stay. Rewriting a working service to chase benchmarks is how projects die.
Fastify: my default for new APIs
Fastify is what Express would look like if it were designed today. JSON schema validation is built in, serialization is fast because it compiles schemas ahead of time, and the plugin system actually isolates scope the way you’d expect.
import Fastify from 'fastify';
const app = Fastify({ logger: true });
app.post('/orders', {
schema: {
body: {
type: 'object',
required: ['sku', 'qty'],
properties: {
sku: { type: 'string', minLength: 3 },
qty: { type: 'integer', minimum: 1 }
}
}
}
}, async (req, reply) => {
const order = await createOrder(req.body);
return reply.code(201).send(order);
});
await app.listen({ port: 3000 });
That schema does three jobs: it validates input before your handler runs, it strips unknown fields so nobody smuggles extra properties into your database, and it speeds up response serialization. In Express you’d install three packages and wire them together yourself.
The tradeoff is a smaller middleware ecosystem and a plugin API that takes an afternoon to understand. Worth it for anything greenfield.
NestJS: when your team needs a constitution
NestJS is opinionated in the way Angular is opinionated. Modules, dependency injection, decorators, guards, interceptors. If you have four developers and they all structure things differently, NestJS ends that argument. If you’re one person building a 12-endpoint API, it’s a lot of ceremony for very little payoff.
@Controller('invoices')
export class InvoicesController {
constructor(private readonly invoices: InvoicesService) {}
@Get(':id')
@UseGuards(AuthGuard)
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.invoicesService.findOne(id);
}
}
Where it earns its keep: long-lived codebases with turnover, teams that need consistent testing patterns, and anything that maps cleanly onto GraphQL or microservices. It runs on top of Express or Fastify, so you’re not giving up performance, just adding structure. The cost is a real learning curve and a lot of files per feature.
Hono: the one that surprised me
Hono is tiny, uses Web Standard Request and Response objects, and runs on Node, Bun, Deno, Cloudflare Workers, and a handful of others without code changes. It’s the framework I reach for when I want the same routing file deployed to a serverless edge and to a plain Node process.
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
app.post('/subscribe',
zValidator('json', z.object({ email: z.string().email() })),
async (c) => {
const { email } = c.req.valid('json');
await addToMailingList(email);
return c.json({ ok: true }, 201);
}
);
export default app;
It’s not going to replace NestJS at a bank, and its ecosystem is young. But for APIs where cold start and deploy flexibility matter, it’s genuinely better than the alternatives, and the code is readable six months later.
Honorable mentions and what I’d actually skip
- Koa: elegant, but it lost the momentum war. You’ll be writing more yourself for no real gain over Fastify.
- AdonisJS: a full MVC framework with an ORM, auth, and CLI baked in. Great if you want Rails-style batteries included and don’t mind buying into one vendor’s whole worldview.
- tRPC: not really a framework, but if your frontend is TypeScript and you own both ends, it removes an entire category of bugs. Pair it with Fastify or Hono.
- Meteor, Sails, LoopBack: mostly legacy at this point. Fine to maintain, hard to justify starting new.
How I actually decide
Three questions, in order:
- Who maintains this in two years? If the answer is “whoever we hire,” pick Express or NestJS. Familiarity beats benchmarks when you’re not the one touching it.
- Does it run at the edge or in a container? Edge or serverless: Hono. Long-running container: Fastify or NestJS.
- How many endpoints and how much shared logic? Under twenty endpoints with simple logic: Fastify or even raw Node. Dozens of endpoints with cross-cutting auth, logging, and validation concerns: NestJS.
Notice that “which one is fastest on a synthetic benchmark” isn’t on the list. Fastify wins those, and it matters less than you’d think. Your database query is almost always the bottleneck. If you’re working with someone on custom web development and they lead with benchmark charts instead of asking about your team and your hosting, that’s a red flag.
One thing that changed for everyone in 2026
Node’s built-in test runner and the node:sqlite module are stable now. For small services that used to pull in Jest, Mocha, and a database driver, you can run tests and store data with zero dependencies.
import { test } from 'node:test';
import assert from 'node:assert';
import { DatabaseSync } from 'node:sqlite';
test('stores an order', () => {
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE orders (sku TEXT, qty INTEGER)');
db.prepare('INSERT INTO orders VALUES (?, ?)').run('ABC-1', 2);
const row = db.prepare('SELECT qty FROM orders').get();
assert.equal(row.qty, 2);
});
Fewer dependencies means fewer supply chain problems, faster installs, and less to explain to the next person. Worth a look before you reach for a framework-specific testing setup.
FAQ
Is Express dead in 2026?
No. Express 5 is stable, maintained, and still the most widely used Node framework. It’s not the fastest or the most modern, but “boring and known by everyone” is a legitimate engineering advantage, especially for projects you’ll hand off.
Should I rewrite my Express app in Fastify?
Almost never. Rewrites cost months and introduce bugs. If performance is your actual problem, profile first, then consider moving only the hot endpoints. Most Express apps that feel slow are waiting on slow queries or missing indexes.
What about Bun and Deno? Do I still need Node frameworks?
Bun and Deno both run Node code well enough now that they’re reasonable choices, and Hono runs on all three. The framework question is mostly separate from the runtime question. Pick the framework that fits your team, then pick the runtime that fits your deploy target.


