Edge Framework

Hono

Hono est un framework web ultra-rapide basé sur les objets standards Request et Response. Écrivez une seule fois, déployez sur Cloudflare Workers, Deno, Bun ou Node, avec un typage de bout en bout via 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;
Sortie
2021
S'exécute sur
Workers, Deno, Bun, Node
Style
Minimaliste, standards web
Idée centrale
Request et Response
Langage
TypeScript
Version
4.x

Pourquoi c'est important

Pourquoi les runtimes edge adorent Hono

Ultra-rapide sur n'importe quel runtime

Un routeur minuscule sans dépendances Node qui démarre en quelques millisecondes et n'ajoute presque rien au démarrage à froid (cold start), ce qui est crucial pour le serverless et l'edge.

Un seul codebase, plusieurs plateformes

La même application s'exécute sur Cloudflare Workers, Deno Deploy, Bun, Vercel et Node. La portabilité est l'objectif de conception, pas une réflexion après coup.

Types de bout en bout avec RPC

Exportez le type d'une route et le client infère chaque chemin, paramètre et réponse. Pas de génération de code ni de types d'API écrits à la main.

Le tableau complet

Les trois piliers de Hono

Utiliser les objets Request et Response de la plateforme web, composer les comportements avec des middleware, et inférer les types client directement depuis les routes du serveur.

Standards web

Portable

Les handlers reçoivent une Request standard et retournent une Response ; le framework ajoute donc le routage et des utilitaires sans inventer de nouvelles primitives.

Middleware

Composer

De petites fonctions asynchrones s'exécutent avant ou après le handler et peuvent lire et écrire dans le contexte.

RPC

Inférer

L'arbre de routage est un type, ainsi le client et le serveur partagent une source de vérité unique pour les structures de données et les codes de statut.

HTML5 en un coup d'oeil

Ce qui est inclus

Routage

Chemins de style Express avec paramètres, wildcards et groupes de routes.

Middleware

app.use chaîne des fonctions asynchrones avec un objet de contexte partagé.

Contexte

c.req lit l'entrée et c.json, c.text et c.html écrivent la sortie.

Middleware intégrés

cors, logger, bearerAuth, cache, etag et secureHeaders sont inclus dans le cœur.

Validateurs

Des validateurs zod et valibot officiels typent le corps analysé.

Adaptateurs

Servez la même application sur Workers, Node, Deno, Bun et plus encore.

Le guide complet

Hono: Tout ce que vous devez savoir

Qu’est-ce que Hono ?

Hono est un framework web léger et rapide basé sur la Web Platform. Au lieu d’inventer ses propres objets de requête et de réponse, il utilise les classes standards Request et Response fournies par les navigateurs, Cloudflare Workers, Deno et Bun. Sur cette base, il ajoute le routage, des middleware et un objet de contexte, sans rien ajouter de superflu.

Son nom signifie « flamme » en japonais, et le projet mise tout sur la vitesse : un cœur minuscule, aucune dépendance native à Node et un routeur conçu pour les cold starts. Cette combinaison a fait de Hono le choix par défaut pour les API en edge, où chaque milliseconde de démarrage impacte chaque requête. Comme le contrat d’exécution repose sur les standards du web, la même application s’exécute également sur Node, vous évitant ainsi tout verrouillage propriétaire.

Le routage basé sur les standards du web

Le routage est volontairement familier. Une méthode et un chemin sont mappés vers un handler, les paramètres utilisent :name et les wildcards utilisent *.

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));

Les routeurs peuvent être divisés en sous-applications et montés, ce qui permet aux services plus volumineux de rester organisés :

import { Hono } from "hono";

const api = new Hono();
api.get("/users", listUsers);
api.get("/users/:id", getUser);

app.route("/api", api);

Chaque handler retourne un Response. Les helpers de contexte — c.json, c.text, c.html, c.redirect, c.body — construisent la réponse correcte avec les headers et le statut, et vous pouvez toujours retourner un new Response(...) brut lorsque vous avez besoin d’un contrôle total.

L’objet context

Le seul argument du handler est le contexte, conventionnellement nommé c. Il contient la requête, les helpers de réponse ainsi qu’un espace pour stocker des valeurs propres à la requête actuelle.

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 expose les bindings du runtime : variables d’environnement, namespaces KV, bases de données D1 et buckets R2 sur Workers. c.set et c.get permettent de partager des valeurs entre les middleware et les handlers, et c.var offre un accès typé à celles-ci. Tout ce dont vous avez besoin pour une requête se trouve dans un seul objet, ce qui rend la composition des middleware très simple.

Middleware

Un middleware est une fonction asynchrone qui reçoit le contexte et next. Appelez await next() pour continuer la chaîne, ou retournez une réponse anticipée pour interrompre le flux.

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);

L’assistant createMiddleware ajoute l’inférence de type, et app.use accepte un modèle de chemin afin que le middleware ne s’exécute que là où il est nécessaire. L’ordre d’enregistrement correspond à l’ordre d’exécution, exactement comme dans Express.

Hono propose un ensemble de middlewares utiles intégrés au cœur du framework :

  • cors pour les en-têtes cross-origin.
  • logger pour la journalisation des requêtes.
  • bearerAuth et basicAuth pour l’authentification.
  • cache pour la mise en cache des réponses en edge.
  • etag, compress et secureHeaders pour l’hygiène HTTP.
  • csrf pour la protection contre les failles CSRF.
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 }));

Comme il s’agit de middlewares standards, ils se composent parfaitement avec les vôtres et avec tout autre élément de l’écosystème.

Validation et réponses typées

Hono propose des validateurs natifs pour Zod et Valibot. Ils analysent une cible (json, query, param, form), rejettent les entrées invalides avec un 400, et fournissent au handler une valeur typée.

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);
});

La valeur analysée est typée, ainsi c.req.valid("json") correspond exactement à la structure du schéma. Vous pouvez personnaliser la réponse en cas d’échec à l’aide d’un hook, ce qui permet de garder des corps d’erreur cohérents avec le reste de votre API.

RPC : des types de bout en bout sans codegen

Le mode RPC est la fonctionnalité phare de Hono. Si vous exportez le type de votre application, le client peut en inférer chaque route et chaque réponse directement.

// 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
}

Il n’y a aucun fichier de schéma à synchroniser et aucun client généré. Modifiez une route sur le serveur et le client ne compilera plus tant qu’il n’aura pas été mis à jour, transformant ainsi le décalage de l’API (API drift) en erreur de build. Cela fonctionne idéalement dans un monorepo ou un package partagé où les deux parties peuvent importer le type.

Rendu HTML et JSX

Hono peut servir du HTML directement, soit sous forme de chaînes de caractères, soit via son propre runtime JSX. Le JSX est conçu pour le rendu serveur : pas de DOM virtuel, pas d’hydratation, juste une sortie sous forme de chaîne.

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>`,
  );
});

Pour une expérience de templating plus complète, hono/jsx propose des composants et des layouts, tandis que des helpers comme html gèrent l’échappement. C’est une solution idéale pour les pages rendues côté serveur et pour les fragments HTML retournés par une API edge.

Un seul codebase, plusieurs runtimes

L’objectif est la portabilité. Le code de l’application n’importe jamais de module spécifique à un runtime ; seul le point d’entrée change.

// 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 };

Le même objet app dessert les trois. Cela signifie qu’un service prototypé sur Node peut migrer vers Workers pour des raisons de coût ou de latence sans nécessiter de réécriture, et qu’une application Workers peut s’exécuter dans une suite de tests locale via l’adaptateur Node.

Déploiement à l’edge

Sur Cloudflare Workers, le déploiement se résume à une commande Wrangler et un petit fichier de configuration.

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"

Les bindings définis ici apparaissent sur c.env, et sont entièrement typés si vous les passez comme générique Bindings à new Hono<{ Bindings: Bindings }>(). Vercel, Deno Deploy et Netlify disposent chacun d’un adaptateur documenté, et la même application se déploie généralement avec une modification d’une seule ligne dans l’entrée. Gardez à l’esprit les contraintes du runtime : limitez les tâches gourmandes en CPU, évitez les connexions aux bases de données de longue durée et utilisez du stockage natif pour l’edge.

Bonnes pratiques

  • Typez les liaisons et les variables via les génériques Hono afin que c.env et c.get soient sécurisés.
  • Validez chaque entrée avec zValidator et conservez le schéma à côté de la route.
  • Composez vos middleware avec createMiddleware et délimitez leur portée avec un pattern de chemin.
  • Exportez AppType et utilisez hc côté client au lieu de types écrits à la main.
  • Gardez vos handlers légers ; déplacez la logique réutilisable dans des fonctions simples ou des services.
  • Retournez les codes de statut appropriés avec c.json(body, status) plutôt que du 200 systématiquement.
  • Concevez pour le runtime : pas de système de fichiers, CPU limité, stockage edge-native.
  • Testez avec app.request() pour que vos tests n’aient besoin ni de serveur ni de réseau.
test("GET /posts", async () => {
  const res = await app.request("/posts");
  expect(res.status).toBe(200);
});

Erreurs courantes

  • Supposer que les bibliothèques Node fonctionnent sur Workers ; les modules intégrés et les addons natifs ne sont souvent pas compatibles.
  • Maintenir des connexions à la base de données ouvertes dans un runtime edge au lieu d’utiliser HTTP ou des bindings.
  • Oublier de retourner la réponse, ce qui fait que le handler se résout en undefined.
  • Enregistrer cors après les routes auxquelles il devrait s’appliquer.
  • Lire c.req.json() plus d’une fois, ce qui échoue car le flux du corps (body stream) est consommé.
  • Sauter l’étape de validation et faire confiance aux chaînes de caractères c.req.query().
  • Laisser AppType diverger en écrivant manuellement les types côté client au lieu de les importer.
  • Effectuer des tâches lourdes pour le CPU lors d’une requête et atteindre la limite de temps du runtime.

Et après ?

Hono apporte le modèle de routage et de middleware que vous connaissez déjà via Express vers des runtimes qui n’existaient pas lors de la création d’Express. Si vous avez besoin d’un serveur Node pérenne avec une validation basée sur des schémas, comparez-le avec Fastify. Pour comprendre les runtimes ciblés par Hono, consultez le guide sur les bases de Node.js, et lorsque vous serez prêt pour la mise en production, la roadmap backend couvre le déploiement sur le cloud. Ensuite, créez une petite API, déployez-la sur Workers et appelez-la avec un client RPC typé.

En pratique

Routes, middleware, validation et RPC

Un service Hono sous quatre angles : un routeur, un middleware réutilisable, un handler validé et un client typé.

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;

Compromis

Hono est-il le bon framework pour vous ?

Hono est optimisé pour les standards, la portabilité et les démarrages à froid. Ce profil est idéal pour l'edge et moins pertinent pour un serveur Node classique à longue durée de vie.

Strengths

  • Vraiment portable

    Le même code s'exécute sans modification sur Workers, Deno, Bun, Node et plusieurs plateformes, ce qui vous laisse toutes les options ouvertes à mesure que l'hébergement évolue.

  • Minuscule et rapide

    Le cœur ne pèse que quelques kilo-octets sans dépendances Node intégrées, il se bundle donc proprement et démarre instantanément.

  • Types sans codegen

    Le mode RPC donne au client une connaissance complète des routes et des réponses directement depuis les types du serveur, détectant tout décalage d'API à la compilation.

Trade-offs

  • L'écosystème est plus jeune

    Hono possède une bibliothèque de middleware en pleine croissance, mais rien de comparable aux décennies de packages d'Express. Vous devrez écrire plus de code de liaison vous-même.

  • Le stockage edge est différent

    Workers n'ont pas de système de fichiers local et un temps CPU limité. Vous devez concevoir autour de KV, D1 ou R2 plutôt qu'une connexion base de données traditionnelle.

  • Toutes les bibliothèques Node ne fonctionnent pas

    Le code qui dépend des modules intégrés de Node peut ne pas s'exécuter dans le runtime Workers. Sur Node via l'adaptateur, cela fonctionne, mais la portabilité n'est pas automatique.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre Hono ?

Notre tutoriel interactif vous guide à travers Hono pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.