Was ist Hono?
Hono ist ein kleines, schnelles Web-Framework, das auf der Web Platform aufbaut. Anstatt eigene Request- und Response-Objekte zu erfinden, nutzt es die Standard-Klassen Request und Response, die Browser, Cloudflare Workers, Deno und Bun bereitstellen. Auf diesem Fundament fügt es Routing, middleware und ein Context-Objekt hinzu – und sonst nichts, was man nicht explizit anfordert.
Der Name bedeutet auf Japanisch „Flamme“, und das Projekt setzt konsequent auf Geschwindigkeit: ein winziger Core, keine integrierten Node-Abhängigkeiten und ein Router, der für Cold Starts optimiert ist. Diese Kombination macht Hono zur ersten Wahl für Edge-APIs, bei denen jede Millisekunde der Startzeit bei jeder Anfrage ins Gewicht fällt. Da der Runtime-Contract dem Webstandard entspricht, läuft dieselbe Anwendung auch auf Node, sodass man niemals an eine Plattform gebunden ist.
Routing auf Basis von Webstandards
Das Routing wirkt bewusst vertraut. Eine Methode und ein Pfad werden einem Handler zugeordnet, Parameter verwenden :name und Wildcards nutzen *.
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello"));
app.get("/posts", (c) => c.json([]));
app.get("/posts/:id", (c) => c.json({ id: c.req.param("id") }));
app.post("/posts", (c) => c.json({ created: true }, 201));
Router können in Sub-Apps aufgeteilt und gemountet werden, wodurch größere Services organisiert bleiben:
import { Hono } from "hono";
const api = new Hono();
api.get("/users", listUsers);
api.get("/users/:id", getUser);
app.route("/api", api);
Jeder Handler gibt ein Response zurück. Die Context-Helper — c.json, c.text, c.html, c.redirect, c.body — erstellen die korrekte Response inklusive Header und Status, und Sie können jederzeit ein rohes new Response(...) zurückgeben, wenn Sie die volle Kontrolle benötigen.
Das Context-Objekt
Das einzige Argument des Handlers ist der Context, der konventionell c genannt wird. Er enthält den Request, die Response-Helper und einen Ort, an dem Werte für den aktuellen Request gespeichert werden können.
app.post("/posts", async (c) => {
const id = c.req.param("id"); // path parameter
const page = c.req.query("page"); // query string
const body = await c.req.json(); // parsed body
const token = c.req.header("authorization");
c.set("requestId", crypto.randomUUID()); // per-request store
return c.json({ id, page, body, token });
});
c.env stellt die Runtime-Bindings bereit: Umgebungsvariablen, KV-Namespaces, D1-Datenbanken und R2-Buckets auf Workers. c.set und c.get teilen Werte zwischen Middleware und Handlern, und c.var bietet einen typisierten Zugriff auf diese. Alles, was für einen Request benötigt wird, befindet sich in einem einzigen Objekt, was die Zusammensetzung von Middleware sehr einfach macht.
Middleware
Eine Middleware ist eine async-Funktion, die den Kontext und next erhält. Rufe await next() auf, um die Kette fortzusetzen, oder gib vorzeitig einen Wert zurück, um den Prozess abzubrechen.
import { createMiddleware } from "hono/factory";
export const timing = createMiddleware(async (c, next) => {
const start = performance.now();
await next();
c.header("Server-Timing", `app;dur=${performance.now() - start}`);
});
app.use("*", timing);
Der createMiddleware-Helper fügt Typinferenz hinzu, und app.use akzeptiert ein Pfadmuster, sodass die Middleware nur dort ausgeführt wird, wo sie benötigt wird. Die Reihenfolge der Registrierung entspricht der Ausführungsreihenfolge, genau wie in Express.
Hono liefert ein nützliches Set an Middleware im Core aus:
corsfür Cross-Origin-Header.loggerfür das Request-Logging.bearerAuthundbasicAuthfür die Authentifizierung.cachefür Edge-Response-Caching.etag,compressundsecureHeadersfür HTTP-Hygiene.csrffür den Schutz vor Cross-Site Request Forgery.
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { bearerAuth } from "hono/bearer-auth";
app.use("*", logger());
app.use("/api/*", cors());
app.use("/admin/*", bearerAuth({ token: c.env.ADMIN_TOKEN }));
Da es sich hierbei um einfache Middleware handelt, lassen sie sich mit deinen eigenen Implementierungen und allem anderen im Ökosystem kombinieren.
Validierung und typisierte Antworten
Hono bietet First-Party-Validator für Zod und Valibot. Diese parsen ein Ziel (json, query, param, form), lehnen ungültige Eingaben mit einem 400 ab und übergeben dem Handler einen typisierten Wert.
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
const CreatePost = z.object({
title: z.string().min(1),
body: z.string(),
});
app.post("/posts", zValidator("json", CreatePost), (c) => {
const post = c.req.valid("json");
return c.json({ id: crypto.randomUUID(), ...post }, 201);
});
Der geparste Wert ist typisiert, sodass c.req.valid("json") exakt der Form des Schemas entspricht. Sie können die Antwort bei Fehlern über einen Hook anpassen, wodurch Sie sicherstellen, dass die Error-Bodies konsistent mit dem Rest Ihrer API bleiben.
RPC: End-to-End-Typisierung ohne Codegen
Der RPC-Modus ist das herausragende Feature von Hono. Wenn du den Typ deiner App exportierst, kann der Client jede Route und Antwort direkt daraus ableiten.
// server.ts
const route = app
.get("/api/users", (c) => c.json([{ id: "1", name: "Ada" }]))
.post(
"/api/users",
zValidator("json", CreateUser),
(c) => c.json({ id: crypto.randomUUID(), ...c.req.valid("json") }, 201),
);
export type AppType = typeof route;
// client.ts
import { hc } from "hono/client";
import type { AppType } from "./server";
const client = hc<AppType>("https://api.example.com");
const res = await client.api.users.$post({
json: { name: "Ada", email: "[email protected]" },
});
if (res.ok) {
const user = await res.json(); // typed from the server handler
}
Es gibt keine Schema-Datei, die synchron gehalten werden muss, und keinen generierten Client. Ändere eine Route auf dem Server, und der Client lässt sich nicht mehr kompilieren, bis er aktualisiert wurde – so wird API-Drift zu einem Build-Fehler. Dies funktioniert am besten in einem Monorepo oder einem Shared Package, in dem beide Seiten den Typ importieren können.
HTML und JSX rendern
Hono kann HTML direkt ausliefern, entweder als Strings oder mit seiner eigenen JSX-Runtime. Das JSX ist speziell für das Server-Rendering konzipiert: kein virtueller DOM, keine Hydration, sondern lediglich String-Output.
import { Hono } from "hono";
import { html } from "hono/html";
const app = new Hono();
app.get("/", (c) => {
return c.html(
html`<!doctype html>
<html>
<body><h1>Hello from ${c.req.query("name") ?? "the edge"}</h1></body>
</html>`,
);
});
Für ein umfassenderes Templating-Erlebnis bietet hono/jsx Komponenten und Layouts, während Helper wie html das Escaping übernehmen. Dies eignet sich hervorragend für servergerenderte Seiten und für HTML-Fragmente, die von einer Edge-API zurückgegeben werden.
Eine Codebasis, viele Runtimes
Die Portabilität ist hier der entscheidende Punkt. Der Anwendungscode importiert niemals ein runtime-spezifisches Modul; nur der Entry Point ändert sich.
// Node
import { serve } from "@hono/node-server";
import app from "./app";
serve({ fetch: app.fetch, port: 3000 });
// Cloudflare Workers
export default app;
// Bun
export default { port: 3000, fetch: app.fetch };
Dasselbe app-Objekt bedient alle drei. Das bedeutet, dass ein auf Node prototypisierter Service aus Kosten- oder Latenzgründen ohne Rewrite zu Workers verschoben werden kann und eine Workers-App über den Node-Adapter in einer lokalen Test-Suite ausgeführt werden kann.
Deployment an die Edge
Bei Cloudflare Workers erfolgt das Deployment über einen Wrangler-Befehl und eine kleine Konfigurationsdatei.
pnpm add -D wrangler
npx wrangler deploy
# wrangler.toml
name = "api"
main = "src/index.ts"
compatibility_date = "2026-09-01"
[[kv_namespaces]]
binding = "KV"
id = "xxxxxxxxxxxxxxxx"
Die hier definierten Bindings erscheinen auf c.env und sind vollständig typisiert, wenn Sie diese als Bindings-Generic an new Hono<{ Bindings: Bindings }>() übergeben. Vercel, Deno Deploy und Netlify verfügen jeweils über einen dokumentierten Adapter; in der Regel lässt sich dieselbe App mit einer einzigen Änderung im Entry-Point deployen. Beachten Sie dabei die Einschränkungen der Runtime: Begrenzen Sie CPU-intensive Aufgaben, vermeiden Sie langlebige Datenbankverbindungen und nutzen Sie edge-native Storage-Lösungen.
Best Practices
- Typisiere Bindings und Variablen über die
HonoGenerics, damitc.envundc.gettypsicher sind. - Validiere jeden Input mit
zValidatorund platziere das Schema direkt neben der Route. - Komponiere middleware mit
createMiddlewareund beschränke den Gültigkeitsbereich über ein Pfad-Pattern. - Exportiere
AppTypeund verwendehcauf dem Client anstelle von manuell geschriebenen Typen. - Halte Handler schlank; verschiebe wiederverwendbare Logik in einfache Funktionen oder Services.
- Gib mit
c.json(body, status)die richtigen Status-Codes zurück, anstatt immer nur 200 zu senden. - Designe für die Runtime: kein Dateisystem, begrenzte CPU, edge-native Storage.
- Teste mit
app.request(), sodass Tests weder einen Server noch ein Netzwerk benötigen.
test("GET /posts", async () => {
const res = await app.request("/posts");
expect(res.status).toBe(200);
});
Häufige Fehler
- Die Annahme, dass Node-Bibliotheken auf Workers funktionieren; Built-ins und native Addons tun dies oft nicht.
- Offenhalten von Datenbankverbindungen in einer Edge-Runtime anstatt HTTP oder Bindings zu verwenden.
- Vergessen, die Response zurückzugeben, sodass der Handler zu
undefinedaufgelöst wird. - Registrierung von
corsnach den Routen, auf die sie angewendet werden soll. - Mehrfaches Lesen von
c.req.json(), was fehlschlägt, da der Body-Stream bereits verbraucht wurde. - Überspringen der Validierung und blindes Vertrauen in
c.req.query()-Strings. - Zulassen von
AppType-Drift durch manuelles Schreiben von Client-Typen anstatt diese zu importieren. - Ausführen von CPU-intensiven Aufgaben innerhalb eines Requests, wodurch das Zeitlimit der Runtime erreicht wird.
Wie geht es weiter?
Hono bringt das Routing- und middleware-Modell, das Sie bereits von Express kennen, in Runtimes, die es zu der Zeit, als Express geschrieben wurde, noch nicht gab. Wenn Sie einen langlebigen Node-Server mit Schema-First-Validierung benötigen, vergleichen Sie es mit Fastify. Um die Runtimes zu verstehen, auf die Hono abzielt, lesen Sie den Guide zu den Node.js basics. Wenn Sie bereit für den Release sind, deckt die backend roadmap das Cloud-Deployment ab. Bauen Sie anschließend eine kleine API, deployen Sie diese auf Workers und rufen Sie sie mit einem typisierten RPC-Client auf.