Skip to main content

Hono.js: The Edge Framework Replacing Express 2026

·PkgPulse Team
0

TL;DR

Hono is now the default choice for new Node.js/edge API projects. It runs on every JavaScript runtime with zero code changes, has first-class TypeScript with end-to-end type safety (RPC mode), ships ~14KB, and benchmarks at 3-5x faster than Express. The migration from Express is straightforward — the API is familiar. The edge-first design means Cloudflare Workers, Bun, Vercel Edge, and Deno all work natively. If you're starting a new backend project in 2026 and don't have a specific reason to use Express or Fastify, start with Hono.

Key Takeaways

  • Multi-runtime: same code runs on Node.js, Bun, Deno, Cloudflare Workers, Vercel Edge, AWS Lambda
  • Performance: ~3-5x faster than Express; comparable to Fastify in benchmarks
  • Bundle size: ~14KB total — Express is ~200KB with its common deps
  • TypeScript RPC: hc() client gives end-to-end type safety like tRPC, without the overhead
  • Download growth: 1K/week (2022) → 5M/week (2024) → 20M/week (2026)

Why Hono Won

The JavaScript backend landscape (2026):

Express (2010):
  → 35M weekly downloads (mostly legacy, slow growth)
  → Node.js only
  → CommonJS first, ESM support added later
  → No TypeScript types (DefinitelyTyped separate)
  → ~200KB with common middleware
  → Middleware ecosystem is huge but unmaintained
  → 9+ years between v4 and v5

Fastify (2016):
  → 10M weekly downloads
  → Node.js only (some Bun support)
  → Fast (schema-based serialization)
  → TypeScript support
  → Good for Node.js-only projects

Hono (2022):
  → 20M weekly downloads and growing fast
  → Every JavaScript runtime
  → TypeScript-first from day one
  → Web Standards API (Request/Response)
  → 14KB total
  → RPC mode for type-safe clients
  → Built-in middleware: auth, CORS, rate limit, logger, etc.

The shift happened when:
1. Cloudflare Workers went mainstream (Node.js APIs don't work there)
2. Bun adoption grew (wanted fast + standard)
3. Edge deployment became standard (Vercel Edge, Cloudflare Workers)
4. TypeScript became the default (not an add-on)

Getting Started: Hono vs Express Side by Side

// ─── Express ───
import express from 'express';

const app = express();
app.use(express.json());

app.get('/users/:id', async (req, res) => {
  const user = await getUser(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; // untyped
  const user = await createUser({ name, email });
  res.status(201).json(user);
});

app.listen(3000);

// ─── Hono ───
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const app = new Hono();

app.get('/users/:id', async (c) => {
  const user = await getUser(c.req.param('id'));
  if (!user) return c.json({ error: 'Not found' }, 404);
  return c.json(user);
});

app.post('/users',
  zValidator('json', z.object({
    name: z.string().min(1),
    email: z.string().email(),
  })),
  async (c) => {
    const { name, email } = c.req.valid('json'); // fully typed!
    const user = await createUser({ name, email });
    return c.json(user, 201);
  }
);

export default app;
// Note: no app.listen() — Hono exports a fetch handler
// The runtime (Node.js/Bun/Cloudflare) handles the server

Multi-Runtime: Same Code Everywhere

// One Hono app, multiple deployment targets:

// app.ts (your actual app — same for all runtimes):
import { Hono } from 'hono';

export const app = new Hono()
  .get('/health', (c) => c.json({ status: 'ok' }))
  .get('/users', async (c) => {
    const users = await db.user.findMany();
    return c.json(users);
  });

export type AppType = typeof app;

// ─── Node.js ───
// index.node.ts:
import { serve } from '@hono/node-server';
import { app } from './app';
serve({ fetch: app.fetch, port: 3000 });

// ─── Bun ───
// index.bun.ts:
import { app } from './app';
export default app; // Bun uses default export with .fetch

// ─── Cloudflare Workers ───
// worker.ts:
import { app } from './app';
export default app; // Workers use default export with .fetch

// ─── Vercel Edge ───
// api/[[...route]].ts:
import { handle } from 'hono/vercel';
import { app } from '../../app';
export const GET = handle(app);
export const POST = handle(app);

// ─── AWS Lambda ───
// lambda.ts:
import { handle } from 'hono/aws-lambda';
import { app } from './app';
export const handler = handle(app);

// The same app.ts runs everywhere with a thin adapter layer.
// Move from Node.js to Cloudflare Workers: change 3 lines.

Hono RPC: End-to-End Type Safety

// Hono's RPC feature — type-safe client like tRPC but lighter:

// server/routes/users.ts:
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const UserRoutes = new Hono()
  .get('/:id', async (c) => {
    const user = await getUser(c.req.param('id'));
    return c.json({ user });
  })
  .post('/',
    zValidator('json', z.object({ name: z.string(), email: z.string().email() })),
    async (c) => {
      const data = c.req.valid('json');
      const user = await createUser(data);
      return c.json({ user }, 201);
    }
  );

export type UserRoutesType = typeof UserRoutes;

// server/index.ts:
import { Hono } from 'hono';
const app = new Hono().route('/users', UserRoutes);
export type AppType = typeof app;

// client.ts (frontend or another service):
import { hc } from 'hono/client';
import type { AppType } from './server';

const client = hc<AppType>('http://localhost:3000');

// Fully typed — no manual type declarations:
const response = await client.users[':id'].$get({ param: { id: '123' } });
const { user } = await response.json();
// user: inferred from server return type ✓

const createResponse = await client.users.$post({
  json: { name: 'Alice', email: 'alice@example.com' }
});
// TypeScript validates the request body against the Zod schema ✓
// TypeScript error if wrong body shape ✓

// vs tRPC: no separate router definition, no adapter, lighter setup
// Works with any HTTP client (not just hc — curl, fetch, etc.)

Built-In Middleware

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { rateLimiter } from 'hono/rate-limiter';
import { bearerAuth } from 'hono/bearer-auth';
import { compress } from 'hono/compress';
import { secureHeaders } from 'hono/secure-headers';
import { etag } from 'hono/etag';

const app = new Hono();

// All built in — zero additional dependencies:
app.use('*', logger());
app.use('*', cors({ origin: ['https://myapp.com'] }));
app.use('*', secureHeaders());   // CSP, HSTS, X-Frame-Options, etc.
app.use('*', compress());        // gzip/brotli
app.use('*', etag());            // ETag caching

// Rate limiting:
app.use('/api/*', rateLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  limit: 100,
  keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',
}));

// Auth:
app.use('/admin/*', bearerAuth({ token: process.env.ADMIN_TOKEN! }));

// JWT auth:
import { jwt } from 'hono/jwt';
app.use('/protected/*', jwt({ secret: process.env.JWT_SECRET! }));

// Compare to Express:
// cors → npm install cors
// helmet → npm install helmet (security headers)
// morgan → npm install morgan (logging)
// express-rate-limit → npm install express-rate-limit
// jsonwebtoken → npm install jsonwebtoken
// 5+ packages vs 0 additional packages with Hono

Performance

Benchmark: HTTP requests/second (Node.js 22, M2 MacBook Pro)
Route: GET /users/:id with JSON response

Framework          req/s    p99 latency
─────────────────────────────────────────
Hono               98,200   1.2ms
Fastify            91,400   1.4ms
Express            28,600   4.8ms
Koa                31,200   4.2ms

Cloudflare Workers (edge, same Hono app):
                  120,000+   0.8ms

Why Hono is fast:
→ Web Standards (Request/Response) = no translation layer
→ Trie-based router (O(log n) route matching vs Express's O(n))
→ No legacy CommonJS overhead
→ Small middleware stack — each middleware adds minimal overhead
→ JIT-friendly code patterns

Compare Hono, Express, Fastify, and other Node.js framework download trends at PkgPulse.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.