A developer benchmarked Express vs Hono on Cloudflare Workers. Express: not compatible. Hono: 100,000+ requests/second with sub-millisecond cold starts. And it runs on Bun, Deno, Node.js, and every edge platform.
Why Hono
Hono is a tiny web framework (14KB) that runs everywhere:
- Cloudflare Workers — free tier: 100K requests/day
- Deno Deploy — free tier: 100K requests/day
- Bun — local development, self-hosted
- Node.js — drop-in Express replacement
- AWS Lambda, Vercel Edge, Netlify Edge
- Fastly Compute
Quick Start
npm create hono@latest my-api
cd my-api
npm install
npm run dev
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.json({ message: 'Hello, Hono!' }));
app.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ id, name: `User ${id}` });
});
export default app;
Full REST API
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
// Middleware
app.use('/api/*', cors());
app.use('/api/*', jwt({ secret: 'your-secret' }));
// Validation schema
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
published: z.boolean().default(false)
});
// Routes
app.get('/api/posts', async (c) => {
const page = Number(c.req.query('page')) || 1;
const limit = Number(c.req.query('limit')) || 10;
// Query database...
return c.json({ posts: [], page, limit });
});
app.post('/api/posts', zValidator('json', createPostSchema), async (c) => {
const data = c.req.valid('json');
// Insert into database...
return c.json({ id: 1, ...data }, 201);
});
app.get('/api/posts/:id', async (c) => {
const id = c.req.param('id');
return c.json({ id, title: 'My Post', content: '...' });
});
app.put('/api/posts/:id', zValidator('json', createPostSchema), async (c) => {
const id = c.req.param('id');
const data = c.req.valid('json');
return c.json({ id, ...data });
});
app.delete('/api/posts/:id', async (c) => {
return c.json({ deleted: true });
});
export default app;
Middleware
import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { timing } from 'hono/timing';
import { secureHeaders } from 'hono/secure-headers';
import { rateLimiter } from 'hono-rate-limiter';
const app = new Hono();
app.use('*', logger());
app.use('*', timing());
app.use('*', secureHeaders());
// Custom middleware
app.use('/api/*', async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
c.header('X-Response-Time', `${ms}ms`);
});
Deploy to Cloudflare Workers (Free)
# wrangler.toml
# name = "my-api"
# compatibility_date = "2026-03-01"
npx wrangler deploy
# Live at: https://my-api.your-subdomain.workers.dev
RPC Client (Type-Safe API Calls)
// Server
const routes = app
.get('/api/users', (c) => c.json([{ id: 1, name: 'Alice' }]))
.post('/api/users', async (c) => {
const body = await c.req.json();
return c.json({ id: 2, ...body }, 201);
});
export type AppType = typeof routes;
// Client (fully typed!)
import { hc } from 'hono/client';
import type { AppType } from './server';
const client = hc<AppType>('https://my-api.workers.dev');
const users = await client.api.users.$get();
const data = await users.json(); // Fully typed: { id: number, name: string }[]
Hono vs Express
| Hono | Express |
|---|---|
| 14KB | 200KB+ with deps |
| Runs on edge (Workers, Deno) | Node.js only |
| Built-in TypeScript | @types/express |
| Built-in validation | Need express-validator |
| Sub-ms cold start | 100ms+ cold start |
Need to build APIs for scraping? Check out my web scraping actors on Apify — no-code data collection.
Need a custom API? Email me at spinov001@gmail.com.
Top comments (0)