Express.js turned 13 in 2023. It's still the most-downloaded Node.js web framework by a wide margin — ~35 million weekly downloads — running on servers that have never been updated and powering backend APIs that nobody wants to touch. Fastify emerged as the performance-focused alternative, shipping JSON schema validation and 2-3x faster throughput than Express. Then Hono arrived with a different premise entirely: a web framework that runs anywhere the Fetch API runs — Node.js, Bun, Deno, Cloudflare Workers, Vercel Edge, AWS Lambda.
In 2026, the question isn't "which is fastest." It's "which runtime do you need to target?"
TL;DR
New Node.js API: Use Fastify — schema validation, serialization, plugin lifecycle, and 2-3x faster than Express. Edge/multi-runtime: Use Hono — runs on Cloudflare Workers, Bun, Deno, and Node.js from a single codebase. Existing Express codebase: Express is fine; migrate to Fastify when performance becomes a bottleneck. Don't rewrite working code without a reason.
Key Takeaways
- Express: ~35M weekly downloads — still dominant by volume, but essentially in maintenance mode
- Fastify: ~4M weekly downloads, ~33K stars — the performance-first Node.js framework
- Hono: ~3M weekly downloads, ~22K stars — fastest growth, edge-native, WinterCG-compliant
- Throughput: Hono ≈ Fastify (120-150K RPS) >> Express (~40K RPS) on hello-world benchmarks
- Real-world difference narrows with database queries and business logic — all frameworks converge around 10-15K RPS with a real DB call
- TypeScript DX: Hono and Fastify have excellent TS support; Express types are an afterthought
At a Glance
| Express 5 | Fastify 5 | Hono 4 | |
|---|---|---|---|
| Version | 5.0 | 5.0 | 4.x |
| GitHub stars | ~65K | ~33K | ~22K |
| Weekly downloads | ~35M | ~4M | ~3M |
| Hello-world RPS | ~40K | ~120-150K | ~120-160K |
| TypeScript | Community types | Built-in | Built-in, excellent |
| Schema validation | Manual | JSON Schema | Zod/Valibot adapters |
| Edge runtime | ❌ | ❌ | ✅ |
| Bundle size | ~200KB | ~300KB | ~14KB |
| Middleware ecosystem | Massive (1000+) | Large | Growing fast |
| Learning curve | Minimal | Medium | Low-Medium |
| Best for | Legacy, simple APIs | High-perf Node.js APIs | Edge, multi-runtime, new projects |
Express: The Default That Refuses to Die
Express is the jQuery of Node.js web frameworks. Almost every tutorial, bootcamp, and "build a REST API" blog post uses Express. The ecosystem is enormous. If something needs to be done in Express, there's a package for it.
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)
})
app.post('/users', async (req, res) => {
const { name, email } = req.body
// No built-in validation — you add your own
const user = await db.users.create({ name, email })
res.status(201).json(user)
})
app.listen(3000)
Express 5 (released 2024): After years of waiting, Express 5 brings native async/await error handling (no more try/catch wrapping for async middleware), dropped support for some legacy patterns, and minor performance improvements. The API is intentionally backward-compatible.
The problems with Express in 2026:
- No built-in schema validation — adds latency and bugs
- Slow JSON serialization (uses
JSON.stringifywithout fast-path optimization) - No TypeScript-native types —
@types/expressis community-maintained - No built-in request/response lifecycle hooks
- Not compatible with edge runtimes (uses Node.js-specific APIs)
When Express is still correct: You have existing middleware that doesn't exist for other frameworks (complex auth flows, legacy payment processing libraries, decades-old session management code). You're a solo developer building an internal tool and the performance headroom is irrelevant. Your team knows Express and onboarding overhead isn't worth the switch.
Fastify: The Right Default for Node.js APIs
Fastify was built with two constraints Express ignores: performance and validation. It ships JSON Schema validation as a first-class feature — define the shape of your request and response, and Fastify validates incoming data and serializes outgoing data using fast-json-stringify, which is 2-3x faster than JSON.stringify.
import Fastify from 'fastify'
const fastify = Fastify({ logger: true })
// Schema-first approach — validation + serialization in one
const userSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
},
additionalProperties: false,
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
}
fastify.post('/users', { schema: userSchema }, async (request, reply) => {
// request.body is validated and typed
const user = await db.users.create(request.body)
return reply.code(201).send(user)
})
await fastify.listen({ port: 3000 })
Fastify's plugin lifecycle is one of its strongest features. Plugins can declare dependencies, encapsulate routes, and share decorators — it solves the code organization problem that Express leaves entirely to the developer:
// Fastify plugin encapsulation
fastify.register(async function authRoutes(fastify) {
fastify.addHook('preHandler', authenticate)
fastify.get('/profile', async (request) => {
return request.user // injected by authenticate hook
})
}, { prefix: '/api' })
Fastify 5 (2024): Node.js 20+ required, TypeScript ESM support improved, new type providers for Zod and TypeBox alongside the existing JSON Schema system.
Limitation: Fastify is Node.js only. No Cloudflare Workers, no Bun-native, no Deno Deploy. If you need multi-runtime deployment, look at Hono.
Hono: Edge-Native, Anywhere the Fetch API Runs
Hono's design constraint is radical: implement against the WinterCG Fetch API standard only. No process, no fs, no Node.js built-ins. The result is a framework that runs identically on Node.js, Bun, Deno, Cloudflare Workers, Vercel Edge Functions, Netlify Edge, and AWS Lambda@Edge — often with the same config file.
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
app.post('/users',
zValidator('json', createUserSchema),
async (c) => {
const data = c.req.valid('json') // Fully typed
const user = await db.users.create(data)
return c.json(user, 201)
}
)
// Deploy anywhere — same code:
export default app // Cloudflare Workers
// or: serve(app) // Bun
// or: serve(app) // Deno
// or: const handler = handle(app) // AWS Lambda
Hono RPC is a unique feature — generate a type-safe client from your route definitions with zero code generation:
// server.ts — define routes with return types
const routes = app
.get('/users/:id', async (c) => {
const user = await getUser(c.req.param('id'))
return c.json(user) // TypeScript infers the return type
})
export type AppType = typeof routes
// client.ts — fully typed client, no schema definition needed
import { hc } from 'hono/client'
const client = hc<AppType>('https://api.example.com')
const user = await client.users[':id'].$get({ param: { id: '123' } })
// user is fully typed — same shape as the server response
Bundle size: Hono is ~14KB — orders of magnitude smaller than Express (~200KB) or Fastify (~300KB). On cold-start-sensitive environments like Cloudflare Workers or Lambda, this directly reduces latency.
The ecosystem gap: Hono's middleware library is growing but smaller than Express or Fastify. Complex auth flows, specialized database adapters, and enterprise middleware are better covered by the established frameworks. For new greenfield projects, the gap is increasingly irrelevant — @hono/auth-js, @hono/jwt, @hono/zod-validator cover most common needs.
Performance in Context
Hello world JSON endpoint (requests/second, single core):
Framework Node.js Bun Cloudflare Workers
Express 5: ~40K ~60K ❌ (not supported)
Fastify 5: ~130K ~160K ❌ (not supported)
Hono 4: ~120K ~180K ~1.2M (V8 isolate)
Real-world API (DB query + JSON response, PostgreSQL, same machine):
Express: ~10K RPS
Fastify: ~13K RPS
Hono: ~12K RPS
At real-world scale, all three converge. The hello-world benchmarks
measure JSON serialization overhead, not application performance.
The performance gap narrows dramatically once you add a database call. Choosing Fastify over Express for a 30% throughput gain on a DB-bound API is rarely worth the migration cost. The real reasons to switch:
- Fastify over Express: Built-in validation catches bugs before they ship, schema-based serialization reduces response payload size, plugin lifecycle organizes large codebases
- Hono over either: You need the same code to run on Node.js, Cloudflare Workers, and Bun — or you're building an edge-first architecture
When to Use Each
Express → Legacy Node.js API that works, massive existing middleware needs, team deeply familiar with the ecosystem, internal tools where performance isn't measured.
Fastify → New Node.js service that stays on Node.js, high-throughput APIs where the 2-3x performance matters, applications where request validation is critical (prevents entire classes of bugs), large codebases that benefit from the plugin lifecycle.
Hono → New projects targeting edge deployment, multi-runtime support required (same code runs on Node.js, Workers, Bun, Deno), lightweight APIs with cold-start sensitivity, projects that want Zod-based type safety throughout the request/response cycle, or teams building with Bun as the primary runtime.
Middleware and Ecosystem Maturity
The ecosystem gap between these frameworks is real, but it's closing faster than most developers expect. Express's 1,000+ middleware packages represent fifteen years of accumulated work — packages like passport, helmet, express-rate-limit, express-session, and hundreds of payment and auth integrations are written explicitly for Express's (req, res, next) middleware signature.
Fastify's middleware ecosystem uses a different adapter pattern via @fastify/middie to run Express middleware when needed, which gives it a practical upgrade path. Its native plugin registry covers the most critical production concerns: @fastify/jwt, @fastify/cors, @fastify/rate-limit, @fastify/multipart, and @fastify/swagger for OpenAPI docs. The plugin encapsulation model also means Fastify plugins are more composable than Express middleware — you can scope authentication hooks to specific route prefixes without global middleware pollution.
Hono's @hono/* scoped packages cover JWT, Zod validation, CORS, basic auth, bearer auth, and session management. The critical missing piece is complex session stores and OAuth flows — most production apps still reach for framework-agnostic libraries like jose for JWT or arctic for OAuth, which work fine in Hono's fetch-native environment. The Hono ecosystem's advantage is runtime portability: any @hono/* package works on Cloudflare Workers just as well as it does on Node.js.
Real-World Architecture Patterns
Where each framework truly differentiates is in how they encourage you to organize larger applications. Express applications inevitably end up with flat routers, global middleware arrays, and shared state via req object augmentation — patterns that work at small scale but become painful at 50+ routes.
Fastify's plugin system is its most underappreciated feature. Each plugin runs in an isolated scope: decorators added inside a plugin aren't visible outside it, hooks are scoped, and the dependency declaration system (fastify-plugin with fastify.register) enforces explicit dependency ordering. Teams building multi-service Node.js backends report that Fastify's structure prevents the "middleware order bugs" that plague Express codebases — the kind where a missing app.use(auth) before a router silently exposes endpoints.
Hono's architecture is closer to Express in structure but adds context isolation via c (the context object), which avoids the class of bugs where Express's shared req.user or similar augmentations get accidentally shared across requests. For edge-deployed applications where code isolation per-request is critical (Cloudflare Workers doesn't share memory between requests anyway), Hono's design fits naturally. The .use() middleware chain and c.set() / c.get() context store give you the Express mental model without Node.js-specific assumptions.
Migration Considerations
Migrating from Express to Fastify is the most common transition team attempt. The surface area change is larger than it looks. The most impactful difference isn't the route handler syntax — it's request lifecycle. Express's flat req → middleware array → handler becomes Fastify's eight-hook lifecycle: onRequest, preParsing, preValidation, preHandler, handler, preSerialization, onSend, onResponse. You gain granular control, but existing Express middleware won't slot in directly.
Practical migration advice: don't rewrite all at once. Teams that succeed typically run Express and Fastify side-by-side on different routes for 2-3 months, migrating high-traffic routes to Fastify while keeping complex legacy routes on Express until each can be properly rebuilt with schema validation. The @fastify/express compatibility plugin helps, but it disables some of Fastify's performance optimizations.
Compare Hono, Express, and Fastify package health on PkgPulse.
Related: Hono vs Elysia 2026 · Hono RPC vs tRPC vs ts-rest · Express vs Fastify 2026