Hono vs Fastify vs Express: API Framework 2026
Hono vs Fastify vs Express: The Best API Framework in 2026
TL;DR
The Node.js API framework landscape has a clear three-tier answer in 2026. Express remains the most-used framework in the ecosystem — ~30M weekly downloads — but it's showing its age: no native TypeScript, no edge runtime support, and performance that trails modern alternatives by a wide margin. Fastify is the established performance champion for Node.js server-side APIs — it's ~10x faster than Express in JSON-heavy workloads and ships with native TypeScript, schema validation, and a mature plugin ecosystem. Hono is the breakout star — ultra-lightweight (~14kB), works natively on Cloudflare Workers, Deno, Bun, and Node.js, and has an Express-like API that's trivially easy to learn. For new APIs in 2026, the choice is Fastify (if you need full Node.js power) or Hono (if you need edge runtime or cross-runtime flexibility).
Key Takeaways
- Hono is the fastest-growing API framework in 2026 — from 200K to ~1.2M weekly downloads in 18 months; the edge runtime story is a genuine differentiator
- Fastify processes ~77,000 requests/second vs Express's ~8,000 req/s in JSON throughput benchmarks — a ~10x difference in raw throughput
- Hono runs everywhere: Node.js, Cloudflare Workers, Deno, Bun, AWS Lambda — a single codebase works across all runtimes
- Express has the largest ecosystem — 50,000+ compatible middleware packages, the most tutorials, the most StackOverflow answers
- TypeScript support: Hono ✅ first-class; Fastify ✅ first-class; Express ⚠️ via
@types/express - Validation: Fastify uses JSON Schema (ajv) for ultra-fast validation; Hono uses Zod via middleware; Express requires separate libraries (express-validator, Zod)
The Framework Choice in 2026
Three frameworks dominate the Node.js API space but with very different architectural philosophies:
- Express — convention-over-zero-convention, middleware-first, 14 years of ecosystem
- Fastify — performance-first, schema-driven, Node.js native
- Hono — runtime-agnostic, ultra-lightweight, developer-experience-first
The "right" choice depends less on feature comparison and more on your deployment target, team experience, and performance requirements.
Express
Express.js turned 14 years old in 2024. At ~30M weekly downloads, it's the most-used JavaScript framework ever created. Its strength is also its weakness: the massive ecosystem of middleware makes it flexible but the core framework hasn't kept pace with the JavaScript ecosystem.
What Express Gets Right
Express pioneered the middleware pattern that every framework after it copied:
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.listen(3000)
Every developer who has built a Node.js API knows this pattern. Express's learning curve is the lowest of the three, and the documentation and community resources are unmatched.
Express's 2026 Limitations
No native TypeScript. Express was written in JavaScript and the TypeScript types (@types/express) are community-maintained. The req and res types require manual augmentation for typed params, query strings, and request bodies:
// Required boilerplate for type safety in Express
interface UserParams { id: string }
interface UserQuery { include?: string }
interface UserBody { name: string; email: string }
app.post<UserParams, User, UserBody, UserQuery>(
'/users/:id',
(req, res) => {
// req.body is now typed as UserBody
// req.params is typed as UserParams
}
)
Fastify and Hono infer these types automatically.
No edge runtime support. Express relies on Node.js APIs that don't exist in Cloudflare Workers, Deno Deploy, or browser environments. You can't run Express at the edge without significant adaptation.
Performance ceiling. Express's middleware chain and lack of schema-based serialization cap its throughput. In real-world benchmarks, Express handles ~8,000 requests/second for a JSON endpoint; Fastify handles ~77,000.
When to Choose Express
- You're maintaining an existing Express codebase
- Your team has deep Express knowledge and the ecosystem fit is high
- You need a very specific middleware that only exists in the Express ecosystem
- Developer familiarity is more important than performance
Fastify
Fastify was built with one goal: maximum performance with excellent developer experience. It achieves both through schema-based serialization, an efficient plugin system, and a highly optimized request router.
Fastify's Performance Architecture
Fastify's throughput advantage comes from JSON Schema-based serialization via fast-json-stringify:
import Fastify from 'fastify'
const fastify = Fastify({ logger: true })
fastify.get<{
Params: { id: string }
Reply: { id: string; name: string; email: string }
}>('/users/:id', {
schema: {
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id'],
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
},
}, async (request, reply) => {
const user = await db.users.findById(request.params.id)
return user
})
The schema.response definition tells Fastify to serialize the response using fast-json-stringify instead of JSON.stringify. This pre-compiled serializer is ~2-3x faster than native JSON serialization, which is the primary source of Fastify's throughput advantage.
Fastify's Plugin System
Fastify's plugin system provides true encapsulation — plugins register routes, hooks, and decorators that are scoped to their context:
import Fastify from 'fastify'
import fastifyJwt from '@fastify/jwt'
import fastifyPostgres from '@fastify/postgres'
const fastify = Fastify()
// Auth plugin — adds `request.user` to authenticated routes
fastify.register(fastifyJwt, { secret: process.env.JWT_SECRET })
// Database plugin — adds `fastify.pg` for PostgreSQL queries
fastify.register(fastifyPostgres, { connectionString: process.env.DATABASE_URL })
// Route plugin with auth guard
fastify.register(async function protectedRoutes(fastify) {
fastify.addHook('onRequest', async (request, reply) => {
await request.jwtVerify()
})
fastify.get('/profile', async (request, reply) => {
const { rows } = await fastify.pg.query(
'SELECT * FROM users WHERE id = $1',
[request.user.sub]
)
return rows[0]
})
})
The official @fastify/* plugin ecosystem covers PostgreSQL, MySQL, Redis, JWT, CORS, rate limiting, file uploads, and more — all optimized for Fastify's performance model.
Fastify TypeScript Experience
Fastify has first-class TypeScript support with full type inference for route params, query strings, body, and reply:
interface UserRouteSchema {
Params: { userId: string }
Querystring: { include?: 'posts' | 'followers' }
Body: { name: string; email: string }
Reply: { 200: User; 404: { error: string } }
}
fastify.put<UserRouteSchema>('/users/:userId', async (request, reply) => {
const { userId } = request.params // string ✅
const { include } = request.query // 'posts' | 'followers' | undefined ✅
const { name, email } = request.body // typed ✅
// ...
})
No manual type augmentation required.
Fastify's Limitations
- Learning curve: The schema-first approach and plugin encapsulation model take time to internalize
- Node.js only: Fastify uses Node.js-specific APIs and doesn't run in Cloudflare Workers or Deno without significant adaptation
- Verbosity: Route definitions with full schemas are more verbose than Express or Hono equivalents
Hono
Hono launched in 2021 and has become the framework of choice for edge-first and cross-runtime API development. Its name means "flame" in Japanese — it's small, fast, and focused.
What Makes Hono Different
Hono's core differentiator is true runtime agnosticism. The same codebase runs on:
// Cloudflare Workers
import { Hono } from 'hono'
export default new Hono().get('/', c => c.json({ hello: 'world' }))
// Node.js
import { serve } from '@hono/node-server'
serve(app)
// Deno
Deno.serve(app.fetch)
// Bun
Bun.serve({ fetch: app.fetch })
// AWS Lambda
import { handle } from 'hono/aws-lambda'
export const handler = handle(app)
No code changes required — the runtime adapter handles the differences.
Hono's API
Hono's API is deliberately Express-like, making migration familiar:
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(),
role: z.enum(['admin', 'user']).default('user'),
})
app.post(
'/users',
zValidator('json', createUserSchema),
async (c) => {
const data = c.req.valid('json') // Typed as inferred Zod schema
const user = await db.users.create(data)
return c.json(user, 201)
}
)
app.get('/users/:id', async (c) => {
const id = c.req.param('id')
const user = await db.users.find(id)
if (!user) return c.json({ error: 'Not found' }, 404)
return c.json(user)
})
@hono/zod-validator integrates Zod validation and automatically types the validated body — c.req.valid('json') is typed as the inferred Zod schema with no manual type annotation.
Hono's Built-in Middleware
Hono ships a rich standard library of middleware that works across all runtimes:
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { jwt } from 'hono/jwt'
import { rateLimiter } from 'hono/rate-limiter'
const app = new Hono()
app.use('*', logger())
app.use('*', cors({ origin: ['https://app.example.com'] }))
// JWT protection on specific routes
app.use('/api/*', jwt({ secret: process.env.JWT_SECRET }))
app.get('/api/profile', async (c) => {
const payload = c.get('jwtPayload') // Typed JWT payload
return c.json({ userId: payload.sub })
})
Hono RPC — Type-Safe Client
Hono's RPC feature is exceptional for monorepos — it generates a type-safe client from your Hono routes:
// server.ts — define routes with typed responses
const routes = app
.get('/users', async (c) => {
const users = await db.users.findAll()
return c.json(users)
})
.post('/users', zValidator('json', createUserSchema), async (c) => {
const data = c.req.valid('json')
const user = await db.users.create(data)
return c.json(user, 201)
})
export type AppType = typeof routes
// client.ts — fully typed without code generation
import { hc } from 'hono/client'
const client = hc<AppType>('https://api.example.com')
const users = await client.users.$get() // Return type inferred from server
await client.users.$post({ json: { name: 'Alice', email: 'alice@example.com' } })
This is end-to-end type safety without tRPC's learning curve or GraphQL's schema language.
Hono's Limitations
- Node.js adapter overhead: On pure Node.js, Hono is fast (~45K req/s) but doesn't match Fastify's ~77K req/s peak
- Smaller plugin ecosystem: Fewer official plugins than Fastify; community ecosystem is growing but smaller
- Newer framework: Fewer tutorials, less production history than Express or Fastify
Performance Benchmarks
Real-world benchmark data from the Fastify benchmarks repository and community tests (Node.js 22, JSON response, 4 CPU cores):
| Framework | Requests/sec | Latency (p50) | Latency (p99) |
|---|---|---|---|
| Fastify | ~77,000 | ~1.2ms | ~4.1ms |
| Hono (Node.js) | ~45,000 | ~2.1ms | ~6.8ms |
| Express | ~8,000 | ~11ms | ~38ms |
| Hono (Bun) | ~120,000 | ~0.8ms | ~2.4ms |
| Hono (Cloudflare Workers) | ~200,000+ | varies | varies |
The Bun and Cloudflare numbers reflect their respective runtime optimizations — Bun's V8 replacement and Cloudflare's V8 isolate architecture.
Head-to-Head Comparison
| Factor | Hono | Fastify | Express |
|---|---|---|---|
| npm downloads/week | ~1.2M | ~7.5M | ~30M |
| Bundle size | ~14kB | ~50kB | ~200kB |
| Node.js req/sec | ~45K | ~77K | ~8K |
| Edge runtime | ✅ Native | ❌ | ❌ |
| TypeScript | ✅ First-class | ✅ First-class | ⚠️ via @types |
| Validation | Zod/Valibot | JSON Schema | Manual |
| Plugin ecosystem | Growing | Mature | Massive |
| Learning curve | Low | Medium | Lowest |
| RPC client | ✅ Built-in | ❌ | ❌ |
| WebSockets | ✅ | ✅ | Via ws library |
Recommendations
Use Hono if:
- You're deploying to Cloudflare Workers, Deno Deploy, or other edge runtimes
- You want a cross-runtime codebase (same code runs on Node.js, Bun, Cloudflare, Deno)
- You want an end-to-end type-safe API without tRPC complexity
- You're starting a new project and want the best TypeScript DX
Use Fastify if:
- You're building a Node.js-only API and need maximum throughput
- You want schema-based validation and serialization for performance
- You're building a complex service where plugin encapsulation is valuable
- You need the most mature Node.js-specific ecosystem
Use Express if:
- You're maintaining an existing Express codebase
- You need a specific Express-only middleware with no equivalent in Fastify/Hono
- Your team's Express expertise makes the migration cost unjustifiable
Methodology
- npm download data from npmjs.com API, March 2026 weekly averages
- Benchmark data from fastify/benchmarks GitHub repo and independent community tests
- Framework versions: Hono v4.x, Fastify v5.x, Express v4.x
- Node.js version: 22 LTS for server benchmarks; Bun 1.x for Bun benchmarks
TypeScript Integration and Type Safety
The TypeScript story is one of the sharpest differentiators across these three frameworks.
Express has TypeScript support via @types/express maintained by DefinitelyTyped. These types cover the core API surface well, but route handler types are not end-to-end typed — the Request and Response objects are not typed to your specific route's parameters, body shape, or response schema. You write TypeScript with Express, but you don't get type errors when your route handler returns a different shape than what your API contract specifies.
Fastify's TypeScript integration is deeper. Schema-defined routes use JSON Schema for request validation, and Fastify's TypeBox provider (@fastify/type-provider-typebox) generates TypeScript types from those schemas, giving you typed request.body, request.params, and request.query in route handlers. A change to the schema surfaces immediately as type errors in handlers that use the changed fields. This requires more upfront schema definition work, but the type safety return is significant for APIs where contract stability matters.
Hono has the most ergonomic TypeScript experience. Hono's typed client (hc) generates a fully typed client from your route definitions — the same types that define server routes generate the client API surface. This end-to-end type safety between server and client resembles what tRPC offers, but without requiring RPC-style procedures. For full-stack teams with server and frontend in the same repository, Hono's typed client eliminates an entire category of runtime errors — calling an endpoint with wrong parameter shape or expecting a different response shape than the server returns.
For runtime validation: all three frameworks work with Zod, though integration differs. Hono has official Zod validator middleware (@hono/zod-validator) that integrates with Hono's typed routes. Fastify's native JSON Schema validation is more performant than Zod; TypeBox (which generates JSON Schema from TypeScript types) is the common middle ground. Express requires explicit Zod parsing in each route handler with no framework-level integration.
Practical recommendation: if TypeScript type safety is a priority for a new project, Hono's end-to-end types or Fastify's TypeBox integration are meaningfully superior to Express's type coverage. If you're adding TypeScript to an existing Express codebase, @types/express plus Zod per-route is the incremental path until the type gaps justify a framework migration. The type gap is most visible at the req.body boundary — Express types that body as any, while Hono and Fastify both make the inferred body type available without explicit casting. For teams that have hit enough undefined is not a property of runtime errors caused by unvalidated request bodies, that single improvement often justifies the migration investment on the next greenfield service.
Explore API framework npm trends and health scores on APIScout — download growth, bundle analysis, and dependency health.
Related: REST vs GraphQL vs gRPC vs tRPC 2026 · API Rate Limiting Best Practices 2026 · API Authentication: OAuth2 vs API Keys vs JWT 2026