Express has been the default Node.js web framework since 2010, and in 2026 it is still the framework most Node.js developers learn first. But Fastify has spent years engineering around Express's architectural limits — synchronous middleware, no built-in validation, and a fat stack-per-request model. For new Node.js services, the question is whether Express's ecosystem advantage outweighs Fastify's performance and DX advantages.
What changed in 2026
- Express 5.0 finally shipped stable. After years in beta, Express 5.x adds native async/await error handling (no need for
express-async-errors), removed deprecated APIs, and modernized the router. It is the first major version since 4.x in 2014.
- Fastify 5.x stabilized. Fastify 5 dropped Node.js < 20 support, improved TypeScript types significantly, and ships a full plugin ecosystem including
@fastify/swagger, @fastify/jwt, and @fastify/rate-limit.
- Hono emerged as a lightweight alternative. Hono runs on Cloudflare Workers, Deno, Bun, and Node.js with a tiny footprint. Worth considering for edge/serverless contexts over both Express and Fastify.
- Bun's native HTTP server is now fast enough that some teams skip Express/Fastify entirely for simple services, using Bun's built-in
Bun.serve().
Performance comparison
Fastify's performance advantage is real and well-documented:
| Framework |
Requests/sec (JSON, Node.js 22) |
Overhead vs raw http |
raw node:http |
~80,000 |
baseline |
| Fastify 5 |
~70,000 |
~12% |
| Express 5 |
~30,000 |
~62% |
| NestJS (Fastify adapter) |
~55,000 |
~31% |
Fastify's JSON serialization via fast-json-stringify is a primary driver. Schema-declared response shapes serialize 2–5× faster than JSON.stringify.
Code comparison
// Express 5 — async route (native, no wrapper needed)
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) return res.status(404).json({ error: 'Not found' })
res.json(user)
})
// Errors propagate to error handler automatically in Express 5
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message })
})
// Fastify 5 — typed route with JSON Schema
import Fastify from 'fastify'
const app = Fastify()
const userSchema = {
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id'],
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
}
app.get('/users/:id', { schema: userSchema }, async (req, reply) => {
const user = await db.users.findById(req.params.id)
if (!user) return reply.code(404).send({ error: 'Not found' })
return user // auto-serialized per schema
})
Fastify's schema declaration validates inputs and accelerates outputs. It also auto-generates OpenAPI docs via @fastify/swagger.
Ecosystem and middleware
| Category |
Express 5 |
Fastify 5 |
| Auth (JWT) |
express-jwt, passport |
@fastify/jwt |
| Rate limiting |
express-rate-limit |
@fastify/rate-limit |
| CORS |
cors |
@fastify/cors |
| Static files |
express.static |
@fastify/static |
| Compression |
compression |
@fastify/compress |
| OpenAPI / Swagger |
swagger-ui-express |
@fastify/swagger (schema-driven) |
| Session |
express-session |
@fastify/session |
| Multipart |
multer |
@fastify/multipart |
Express has more third-party middleware due to age. Fastify's official plugin ecosystem covers the vast majority of production needs, and the plugin system (with encapsulation) is architecturally cleaner.
How to pick
- Greenfield Node.js API service? Fastify. Better performance, built-in validation, OpenAPI generation, and TypeScript support make it the better default in 2026.
- Existing Express codebase? Migrate cautiously. Express 5 is a worthwhile upgrade from Express 4 without a framework switch. Full Express-to-Fastify migration requires rewriting all middleware.
- Teams with junior developers or lots of tutorials needed? Express still has more beginner resources, StackOverflow answers, and third-party tutorials.
- NestJS user? NestJS supports both Express and Fastify adapters. Switch to the Fastify adapter for ~2× throughput with the same NestJS DX.
- Edge / Cloudflare Workers? Neither — use Hono, which is built for edge environments.
Common mistakes
Using express-async-errors in Express 5. Express 5 handles async errors natively. The workaround library is no longer needed.
Not declaring Fastify schemas. Fastify without schemas loses most of its performance advantage. Always declare schema for production routes.
Treating Express middleware as Fastify plugins. They are different models. Express middleware mutates req/res in a chain; Fastify plugins use encapsulation and decorators. Port carefully.
Ignoring Fastify's encapsulation. Fastify plugins registered in a scope are not visible outside that scope. This is a feature, not a bug — design your plugin tree intentionally.
What to skip
- Restify — niche and unmaintained in 2026. Fastify replaced its use case.
- Koa — an interesting design, but the ecosystem is small and Fastify's performance/DX wins for most teams.
- Express 4.x for new projects — Express 5 is stable; there is no reason to start on the old major version.
FAQ
Is Fastify production-ready?
Yes. Fastify is used in production by large companies including Platformatic, NearForm, and others. It has been stable since v3 and v5 is battle-tested.
Can I use Express middleware in Fastify?
Via @fastify/middie or @fastify/express compatibility layers, yes — but with a performance penalty. It is a migration bridge, not a long-term architecture.
Does Fastify support TypeScript well?
Yes. Fastify 5 ships full TypeScript generics for request/reply types. Combined with @sinclair/typebox for schema-to-type inference, the DX is excellent.
What about Hapi.js in 2026?
Hapi is still maintained and used by some enterprise teams. It has excellent validation via Joi. But it has a smaller community than both Express and Fastify, and Fastify's performance and ecosystem depth make it the better choice for new projects.
Where to go next