DEV Community

Alex Spinov
Alex Spinov

Posted on

Hono.js Is What Express Should Have Been — Build APIs in 5 Minutes

Express Is 14 Years Old. We Can Do Better.

Express was revolutionary in 2010. But in 2026, it still has no built-in TypeScript support, no native async/await, and middleware ordering bugs that haunt every team.

Hono.js fixes all of this in a 14KB package that runs on every runtime: Node, Bun, Deno, Cloudflare Workers, Vercel, AWS Lambda.

Hello World

import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.text("Hello World"));
app.get("/json", (c) => c.json({ message: "Hello" }));
app.get("/user/:id", (c) => {
  const id = c.req.param("id");
  return c.json({ id, name: "John" });
});

export default app;
Enter fullscreen mode Exit fullscreen mode

That's it. No require, no app.listen(), no callback hell.

Why It's Better Than Express

Feature Express Hono
TypeScript Manual setup Built-in
Bundle size 200KB+ 14KB
Async handlers Needs wrapper Native
Validation Install zod + middleware Built-in validator
OpenAPI docs Install swagger Built-in
Runtime support Node only Node, Bun, Deno, CF Workers, Lambda

Built-in Validation

No more installing express-validator or zod middleware:

import { Hono } from "hono";
import { validator } from "hono/validator";

const app = new Hono();

app.post("/users",
  validator("json", (value, c) => {
    const { name, email } = value;
    if (!name || typeof name !== "string") {
      return c.json({ error: "Name is required" }, 400);
    }
    if (!email?.includes("@")) {
      return c.json({ error: "Invalid email" }, 400);
    }
    return { name, email };
  }),
  (c) => {
    const { name, email } = c.req.valid("json");
    return c.json({ created: { name, email } }, 201);
  }
);
Enter fullscreen mode Exit fullscreen mode

Middleware That Makes Sense

import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { bearerAuth } from "hono/bearer-auth";
import { cache } from "hono/cache";

const app = new Hono();

// Global middleware
app.use("*", logger());
app.use("*", cors());

// Protected routes
app.use("/api/*", bearerAuth({ token: "my-secret" }));

// Cached routes
app.get("/api/data", cache({ cacheName: "my-cache", cacheControl: "max-age=3600" }), (c) => {
  return c.json({ data: "expensive computation" });
});
Enter fullscreen mode Exit fullscreen mode

All built-in. No npm install for each middleware.

Deploy Anywhere

# Cloudflare Workers
npx wrangler deploy

# Bun
bun run src/index.ts

# Node
npx tsx src/index.ts

# Deno
deno run --allow-net src/index.ts
Enter fullscreen mode Exit fullscreen mode

Same code, any runtime. Write once, deploy anywhere.

Real Performance

Hono consistently benchmarks 2-5x faster than Express on Node.js, and even faster on Bun/Deno since it's designed for modern runtimes.

Requests/sec (simple JSON response):
  Express:  18,000
  Fastify:  45,000
  Hono:     52,000
  Hono+Bun: 112,000
Enter fullscreen mode Exit fullscreen mode

Migration From Express

The patterns are almost identical:

// Express
app.get("/users/:id", async (req, res) => {
  const user = await db.getUser(req.params.id);
  res.json(user);
});

// Hono
app.get("/users/:id", async (c) => {
  const user = await db.getUser(c.req.param("id"));
  return c.json(user);
});
Enter fullscreen mode Exit fullscreen mode

The main difference: Hono handlers return responses instead of calling res.json(). Cleaner, more functional, easier to test.

Should You Switch?

Yes if you're starting a new project, need edge deployment, or want TypeScript without config.

Not yet if you have a massive Express codebase with custom middleware — migration cost may not be worth it.

But for anything new in 2026? There's no reason to npm init with Express.


I write about modern dev tools and APIs. Follow for more.


More from me: 10 Dev Tools I Use Daily | 77 Scrapers on a Schedule | 150+ Free APIs

Top comments (0)