DEV Community

Alex Spinov
Alex Spinov

Posted on

Hono Has a Free API — The 14KB Web Framework for Every Runtime

Hono is the ultrafast web framework for the edge — built for Cloudflare Workers, Deno, Bun, and Node.js. It's 3x faster than Express and fits in 14KB.

Why Hono?

  • Ultrafast — 3x faster than Express, comparable to Elysia
  • Tiny — 14KB minified, zero dependencies for core
  • Multi-runtime — Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda
  • Type-safe — full TypeScript with RPC-like client
  • Middleware ecosystem — JWT, CORS, rate limiting, OpenAPI
  • JSX support — server-side rendering built in

Quick Start

# Create a new project
npm create hono@latest my-api

# Or install manually
npm install hono
Enter fullscreen mode Exit fullscreen mode
import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.text("Hello Hono!"));

app.get("/api/users", (c) => {
  return c.json([
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" },
  ]);
});

app.post("/api/users", async (c) => {
  const body = await c.req.json();
  return c.json({ id: 3, ...body }, 201);
});

export default app;
Enter fullscreen mode Exit fullscreen mode

Routing

const app = new Hono();

// Path parameters
app.get("/users/:id", (c) => {
  const id = c.req.param("id");
  return c.json({ id, name: "Alice" });
});

// Query parameters
app.get("/search", (c) => {
  const q = c.req.query("q");
  const page = c.req.query("page") || "1";
  return c.json({ query: q, page });
});

// Wildcard
app.get("/files/*", (c) => {
  const path = c.req.path;
  return c.text(`Serving: ${path}`);
});

// Method chaining
app.route("/api/v1")
  .get("/users", listUsers)
  .post("/users", createUser)
  .get("/users/:id", getUser)
  .put("/users/:id", updateUser)
  .delete("/users/:id", deleteUser);
Enter fullscreen mode Exit fullscreen mode

Middleware

import { Hono } from "hono";
import { cors } from "hono/cors";
import { jwt } from "hono/jwt";
import { logger } from "hono/logger";
import { rateLimiter } from "hono/rate-limiter";
import { secureHeaders } from "hono/secure-headers";

const app = new Hono();

// Global middleware
app.use("*", logger());
app.use("*", secureHeaders());
app.use("*", cors({ origin: "https://myapp.com" }));

// Route-specific middleware
app.use("/api/*", jwt({ secret: "my-secret" }));

// Custom middleware
app.use("/api/*", async (c, next) => {
  const start = Date.now();
  await next();
  const duration = Date.now() - start;
  c.header("X-Response-Time", `${duration}ms`);
});
Enter fullscreen mode Exit fullscreen mode

Validation (Zod Integration)

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(2),
  email: z.string().email(),
  age: z.number().min(18).optional(),
});

app.post(
  "/users",
  zValidator("json", createUserSchema),
  (c) => {
    const data = c.req.valid("json");
    // data is fully typed: { name: string, email: string, age?: number }
    return c.json({ user: data }, 201);
  }
);

// Query validation
const searchSchema = z.object({
  q: z.string().min(1),
  page: z.coerce.number().default(1),
  limit: z.coerce.number().default(10),
});

app.get(
  "/search",
  zValidator("query", searchSchema),
  (c) => {
    const { q, page, limit } = c.req.valid("query");
    return c.json({ query: q, page, limit });
  }
);
Enter fullscreen mode Exit fullscreen mode

RPC Client (Type-Safe API Calls)

// server.ts
const app = new Hono()
  .get("/users", (c) => c.json([{ id: 1, name: "Alice" }]))
  .post("/users", zValidator("json", createUserSchema), (c) => {
    return c.json({ id: 2, ...c.req.valid("json") }, 201);
  });

export type AppType = typeof app;
Enter fullscreen mode Exit fullscreen mode
// client.ts
import { hc } from "hono/client";
import type { AppType } from "./server";

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

// Full type safety + autocomplete!
const users = await client.users.$get();
const data = await users.json(); // typed as { id: number, name: string }[]

const newUser = await client.users.$post({
  json: { name: "Bob", email: "bob@example.com" },
});
Enter fullscreen mode Exit fullscreen mode

JSX (Server-Side Rendering)

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

const app = new Hono();

const Layout = ({ children }: { children: any }) => html`
  <!DOCTYPE html>
  <html>
    <head><title>Hono App</title></head>
    <body>${children}</body>
  </html>
`;

app.get("/", (c) => {
  return c.html(
    <Layout>
      <h1>Hello from Hono!</h1>
      <p>Server-rendered HTML</p>
    </Layout>
  );
});
Enter fullscreen mode Exit fullscreen mode

Deploy Everywhere

// Cloudflare Workers (default)
export default app;

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

// Deno
Deno.serve(app.fetch);

// Bun
export default { fetch: app.fetch, port: 3000 };

// AWS Lambda
import { handle } from "hono/aws-lambda";
export const handler = handle(app);
Enter fullscreen mode Exit fullscreen mode

Hono vs Express vs Fastify vs Elysia

Feature Hono Express Fastify Elysia
Size 14KB 200KB+ 100KB+ 50KB
Speed Very fast Slow Fast Fastest
TypeScript Native @types Native Native
Multi-runtime Yes (5+) Node only Node only Bun only
Validation Zod plugin Manual Ajv Built-in
RPC client Built-in No No Eden Treaty
JSX Built-in EJS/Pug No No

Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.

Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.

Top comments (0)