Express.js was built in 2010. It doesn't support async/await natively, has no built-in TypeScript types, and runs only on Node.js. You patch it with middleware and hope for the best.
What if your web framework was built for 2026 — typed, fast, and ran on every JavaScript runtime?
That's Hono. 14KB, middleware-based, runs on Cloudflare Workers, Deno, Bun, Node.js, Vercel Edge, AWS Lambda, and more.
Why Hono Over Express
| Feature | Express | Hono |
|---|---|---|
| TypeScript | Needs @types/express | Native |
| Runtime support | Node.js only | Node, Deno, Bun, Workers, Lambda |
| Request routing speed | ~8,000 req/s | ~120,000 req/s |
| Bundle size | 1.7 MB | 14 KB |
| Async error handling | Manual try/catch | Built-in |
| Middleware typing | None | Full type inference |
Quick Start
npm create hono@latest my-app
cd my-app && npm run dev
Basic API
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello Hono!"));
app.get("/users/:id", (c) => {
const id = c.req.param("id");
return c.json({ id, name: "Aleksej" });
});
app.post("/users", async (c) => {
const body = await c.req.json();
return c.json({ created: true, user: body }, 201);
});
export default app;
Built-in Middleware
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { bearerAuth } from "hono/bearer-auth";
import { compress } from "hono/compress";
import { cache } from "hono/cache";
const app = new Hono();
app.use("*", logger());
app.use("*", cors());
app.use("*", compress());
app.use("/api/*", bearerAuth({ token: "my-secret-token" }));
app.get("/cached", cache({ cacheName: "my-cache", cacheControl: "max-age=3600" }), (c) => {
return c.json({ data: "this response is cached for 1 hour" });
});
RPC Mode — Type-Safe Client-Server Communication
// server.ts
import { Hono } from "hono";
const app = new Hono()
.get("/users", async (c) => {
const users = await db.users.findMany();
return c.json(users);
})
.post("/users", async (c) => {
const body = await c.req.json<{ name: string; email: string }>();
const user = await db.users.create(body);
return c.json(user, 201);
});
export type AppType = typeof app;
// client.ts — full type inference!
import { hc } from "hono/client";
import type { AppType } from "./server";
const client = hc<AppType>("http://localhost:3000");
const res = await client.users.$get();
const users = await res.json(); // Fully typed!
No code generation. No schema definition. Pure TypeScript inference.
Deploy Anywhere
// Cloudflare Workers — just export
export default app;
// Node.js
import { serve } from "@hono/node-server";
serve(app, { port: 3000 });
// Bun
export default { port: 3000, fetch: app.fetch };
// Deno
Deno.serve(app.fetch);
// AWS Lambda
import { handle } from "hono/aws-lambda";
export const handler = handle(app);
Same code, same app — zero changes across runtimes.
When to Choose Hono
Choose Hono when:
- You deploy to edge/serverless (Cloudflare Workers, Vercel Edge)
- You want Express-like DX with modern TypeScript
- Multi-runtime support matters (deploy same API to different platforms)
- You want type-safe RPC without GraphQL/tRPC complexity
Skip Hono when:
- You need a full-stack framework with SSR/routing (use Remix, Next.js)
- Your team is deep in Express middleware ecosystem
- You need WebSocket support beyond basic (Hono's WS is basic)
The Bottom Line
Hono is what Express would be if built today — tiny, typed, and portable across every JavaScript runtime.
Start here: hono.dev
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)