Edge Framework

Hono

Hono ist ein ultraschnelles Web-Framework, das auf den Standard-Request- und Response-Objekten basiert. Einmal schreiben, ausführen auf Cloudflare Workers, Deno, Bun oder Node, mit End-to-End-Typisierung durch RPC.

intermediate14 min readUpdated 16. Sept. 2026
index.ts
ts
// index.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";

type Bindings = { KV: KVNamespace; JWT_SECRET: string };

const app = new Hono<{ Bindings: Bindings }>();

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

app.get("/", (c) => c.text("Hello from the edge"));

app.get("/api/users/:id", async (c) => {
  const id = c.req.param("id");
  const user = await c.env.KV.get(`user:${id}`, "json");
  if (!user) return c.json({ error: "not_found" }, 404);
  return c.json(user);
});

export default app;
Veröffentlicht
2021
Läuft auf
Workers, Deno, Bun, Node
Stil
Minimal, Web-Standard
Kernidee
Request und Response
Sprache
TypeScript
Version
4.x

Warum es wichtig ist

Warum Edge-Runtimes Hono lieben

Ultraschnell auf jeder Runtime

Ein winziger Router ohne Node-Abhängigkeiten startet in Millisekunden und beeinflusst den Cold Start kaum, was bei Serverless und am Edge entscheidend ist.

Eine Codebasis, viele Plattformen

Die gleiche App läuft auf Cloudflare Workers, Deno Deploy, Bun, Vercel und Node. Portabilität ist das Designziel, kein nachträglicher Gedanke.

End-to-End-Typen mit RPC

Exportiere einen Route-Typ und der Client leitet jeden Pfad, Parameter und Response ab. Keine Codegenerierung und keine manuell geschriebenen API-Typen.

Das Gesamtbild

Die drei Kernideen hinter Hono

Nutze die Request- und Response-Objekte der Web-Plattform, komponiere Verhalten mit middleware und leite Client-Typen direkt aus den Server-Routes ab.

Web-Standards

Portabel

Handler empfangen einen Standard-Request und geben eine Response zurück; das Framework fügt Routing und Helfer hinzu, ohne neue Primitive zu erfinden.

Middleware

Komponieren

Kleine asynchrone Funktionen laufen vor oder nach dem Handler und können den Kontext lesen und schreiben.

RPC

Ableiten

Der Route-Baum ist ein Typ, sodass Client und Server eine gemeinsame Source of Truth für Datenstrukturen und Statuscodes teilen.

HTML5 auf einen Blick

Was im Paket enthalten ist

Routing

Express-ähnliche Pfade mit Parametern, Wildcards und Route-Gruppen.

Middleware

app.use verknüpft asynchrone Funktionen mit einem gemeinsamen Kontext-Objekt.

Kontext

c.req liest den Input, während c.json, c.text und c.html den Output schreiben.

Integrierte Middleware

cors, logger, bearerAuth, cache, etag und secureHeaders sind im Core enthalten.

Validatoren

First-Party zod- und valibot-Validatoren typisieren den geparsten Body.

Adapter

Betreibe die gleiche App auf Workers, Node, Deno, Bun und mehr.

Der vollständige Leitfaden

Hono: Alles was Sie wissen müssen

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:

  • cors für Cross-Origin-Header.
  • logger für das Request-Logging.
  • bearerAuth und basicAuth für die Authentifizierung.
  • cache für Edge-Response-Caching.
  • etag, compress und secureHeaders für HTTP-Hygiene.
  • csrf fü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 Hono Generics, damit c.env und c.get typsicher sind.
  • Validiere jeden Input mit zValidator und platziere das Schema direkt neben der Route.
  • Komponiere middleware mit createMiddleware und beschränke den Gültigkeitsbereich über ein Pfad-Pattern.
  • Exportiere AppType und verwende hc auf 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 undefined aufgelöst wird.
  • Registrierung von cors nach 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.

In der Praxis

Routes, middleware, Validierung und RPC

Ein Hono-Service aus vier Perspektiven: ein Router, eine wiederverwendbare middleware, ein validierter Handler und ein typisierter Client.

routes/users.ts
import { Hono } from "hono";

const users = new Hono();

users.get("/", (c) => c.json({ users: [] }));

users.get("/:id", async (c) => {
  const id = c.req.param("id");
  const user = await getUser(id);
  if (!user) return c.json({ error: "not_found" }, 404);
  return c.json(user);
});

export default users;

Abwägungen

Ist Hono das richtige Framework für dich?

Hono ist auf Standards, Portabilität und Cold Starts optimiert. Das ist ideal am Edge, aber weniger relevant auf einem langlebigen Node-Server.

Strengths

  • Echt portabel

    Der gleiche Code läuft unverändert auf Workers, Deno, Bun, Node und verschiedenen Plattformen, was deine Optionen offen hält, während sich das Hosting weiterentwickelt.

  • Winzig und schnell

    Der Core umfasst nur wenige Kilobyte ohne Node-interne Abhängigkeiten, wodurch er sauber gebündelt wird und sofort startet.

  • Typen ohne Codegen

    Der RPC-Modus gibt dem Client vollständiges Wissen über Routes und Responses direkt aus den Server-Typen, wodurch API-Abweichungen bereits zur Kompilierzeit erkannt werden.

Trade-offs

  • Das Ökosystem ist jünger

    Hono hat eine wachsende Middleware-Bibliothek, aber nicht die jahrzehntelange Paketvielfalt von Express. Du wirst mehr Glue-Code selbst schreiben müssen.

  • Edge-Storage ist anders

    Workers haben kein lokales Dateisystem und begrenzte CPU-Zeit. Du musst dein Design auf KV, D1 oder R2 ausrichten statt auf eine traditionelle Datenbankverbindung.

  • Nicht jede Node-Library funktioniert

    Code, der von Node-Built-ins abhängt, läuft möglicherweise nicht in der Workers-Runtime. Auf Node via Adapter ist das kein Problem, aber Portabilität ist nicht automatisch gegeben.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Hono zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Hono — mit Quizzen und echtem Code, den Sie im Browser ausführen können.