Edge Framework

Hono

Hono es un framework web ultrarrápido construido sobre los objetos estándar Request y Response. Escribe una vez, ejecuta en Cloudflare Workers, Deno, Bun o Node, con tipos end-to-end a través de 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;
Lanzado
2021
Ejecuta en
Workers, Deno, Bun, Node
Estilo
Minimalista, basado en estándares web
Idea central
Request y Response
Lenguaje
TypeScript
Versión
4.x

Por que importa

Por qué los runtimes de edge aman Hono

Ultrarrápido en cualquier runtime

Un router diminuto sin dependencias de Node que arranca en milisegundos y casi no añade tiempo al cold start, algo crucial en serverless y el edge.

Un solo código, múltiples plataformas

La misma aplicación se ejecuta en Cloudflare Workers, Deno Deploy, Bun, Vercel y Node. La portabilidad es el objetivo del diseño, no una ocurrencia posterior.

Tipos end-to-end con RPC

Exporta un tipo de ruta y el cliente infiere cada ruta, parámetro y respuesta. Sin generación de código y sin escribir tipos de API a mano.

La imagen completa

Las tres ideas detrás de Hono

Habla el lenguaje de Request y Response de la plataforma web, compone el comportamiento con middleware e infiere los tipos del cliente directamente desde las rutas del servidor.

Estándares web

Portable

Los handlers reciben un Request estándar y devuelven un Response, por lo que el framework añade routing y helpers sin inventar nuevas primitivas.

Middleware

Componer

Funciones async pequeñas que se ejecutan antes o después del handler y pueden leer y escribir en el contexto.

RPC

Inferir

El árbol de rutas es un tipo, por lo que el cliente y el servidor comparten una única fuente de verdad para las estructuras y los códigos de estado.

HTML5 de un vistazo

Qué incluye de fábrica

Routing

Rutas estilo Express con parámetros, comodines y grupos de rutas.

Middleware

app.use encadena funciones async con un objeto de contexto compartido.

Contexto

c.req lee la entrada y c.json, c.text y c.html escriben la salida.

Middleware integrado

cors, logger, bearerAuth, cache, etag y secureHeaders vienen incluidos en el núcleo.

Validadores

Validadores oficiales de zod y valibot tipan el cuerpo parseado.

Adaptadores

Sirve la misma aplicación en Workers, Node, Deno, Bun y más.

La guia completa

Hono: Todo lo que necesitas saber

¿Qué es Hono?

Hono es un framework web pequeño y rápido construido sobre la Web Platform. En lugar de inventar sus propios objetos de solicitud y respuesta, utiliza las clases estándar Request y Response que proporcionan los navegadores, Cloudflare Workers, Deno y Bun. Sobre esa base, añade enrutamiento, middleware y un objeto de contexto, y nada más de lo que no solicites.

El nombre significa “llama” en japonés, y el proyecto apuesta por la velocidad: un núcleo diminuto, sin dependencias integradas de Node y un router diseñado para cold starts. Esa combinación ha convertido a Hono en la opción predeterminada para APIs en el edge, donde cada milisegundo de arranque se paga en cada solicitud. Debido a que el contrato del runtime es el estándar web, la misma aplicación también se ejecuta en Node, por lo que nunca quedas atrapado en un solo ecosistema.

Enrutamiento basado en estándares web

El enrutamiento resulta deliberadamente familiar. Un método y una ruta se mapean a un handler, los parámetros utilizan :name y los comodines utilizan *.

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

Los routers pueden dividirse en sub-apps y montarse, que es la forma en que los servicios más grandes se mantienen organizados:

import { Hono } from "hono";

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

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

Cada handler devuelve un Response. Los helpers de contexto — c.json, c.text, c.html, c.redirect, c.body — construyen la respuesta correcta con headers y status, y siempre puedes devolver un new Response(...) puro cuando necesites control total.

El objeto context

El único argumento del handler es el context, convencionalmente llamado c. Este contiene la request, los helpers de respuesta y un lugar para almacenar valores para la request actual.

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 expone los bindings del runtime: variables de entorno, namespaces de KV, bases de datos D1 y buckets de R2 en Workers. c.set y c.get comparten valores entre middleware y handlers, y c.var proporciona acceso tipado a ellos. Todo lo que necesitas para una request reside en un solo objeto, lo que hace que la composición de middleware sea sencilla.

Middleware

El middleware es una función asíncrona que recibe el contexto y next. Llama a await next() para continuar la cadena, o retorna prematuramente para interrumpir el flujo.

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

El helper createMiddleware añade inferencia de tipos, y app.use acepta un patrón de ruta para que el middleware solo se ejecute donde sea necesario. El orden de registro es el orden de ejecución, exactamente igual que en Express.

Hono incluye un conjunto de middleware muy útiles en su núcleo:

  • cors para encabezados de origen cruzado.
  • logger para el registro de solicitudes (logging).
  • bearerAuth y basicAuth para autenticación.
  • cache para el almacenamiento en caché de respuestas en el edge.
  • etag, compress y secureHeaders para higiene de HTTP.
  • csrf para protección contra falsificación de solicitudes entre sitios (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 }));

Debido a que son middleware estándar, se componen con los tuyos propios y con cualquier otra herramienta del ecosistema.

Validación y respuestas tipadas

Hono cuenta con validadores nativos para Zod y Valibot. Estos analizan un objetivo (json, query, param, form), rechazan las entradas inválidas con un 400 y proporcionan al handler un valor tipado.

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

El valor analizado está tipado, por lo que c.req.valid("json") tiene exactamente la forma del esquema. Puedes personalizar la respuesta de error mediante un hook, lo que te permite mantener los cuerpos de los errores consistentes con el resto de tu API.

RPC: tipos end-to-end sin codegen

El modo RPC es la característica estrella de Hono. Si exportas el tipo de tu app, el cliente puede inferir cada ruta y respuesta directamente desde él.

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

No hay ningún archivo de esquema que mantener sincronizado ni un cliente generado. Cambia una ruta en el servidor y el cliente fallará al compilar hasta que se actualice, lo que convierte el API drift en un error de build. Esto funciona mejor en un monorepo o en un paquete compartido donde ambas partes puedan importar el tipo.

Renderizado de HTML y JSX

Hono puede servir HTML directamente, ya sea como strings o mediante su propio runtime de JSX. El JSX está diseñado para el renderizado en el servidor: sin DOM virtual, sin hidratación, simplemente una salida de strings.

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

Para una experiencia de plantillas más completa, hono/jsx proporciona componentes y layouts, y helpers como html se encargan del escapado. Es una opción ideal para páginas renderizadas en el servidor y para fragmentos de HTML devueltos por una API en el edge.

Un solo codebase, múltiples runtimes

La portabilidad es la clave. El código de la aplicación nunca importa un módulo específico de un runtime; solo cambia el punto de entrada.

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

El mismo objeto app sirve para los tres. Esto significa que un servicio prototipado en Node puede migrar a Workers por razones de costo o latencia sin necesidad de reescribirlo, y una aplicación de Workers puede ejecutarse en una suite de pruebas local a través del adaptador de Node.

Despliegue en el edge

En Cloudflare Workers, el despliegue se realiza mediante un comando de Wrangler y un pequeño archivo de configuración.

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"

Los bindings definidos aquí aparecen en c.env, totalmente tipados si los pasas como el genérico Bindings a new Hono<{ Bindings: Bindings }>(). Vercel, Deno Deploy y Netlify cuentan cada uno con un adaptador documentado, y normalmente la misma aplicación se despliega cambiando una sola línea de entrada. Recuerda las restricciones del runtime: limita las tareas que consuman mucha CPU, evita las conexiones a bases de datos de larga duración y utiliza almacenamiento nativo de edge.

Mejores prácticas

  • Tipa los bindings y las variables a través de los genéricos de Hono para que c.env y c.get sean seguros.
  • Valida cada entrada con zValidator y mantén el esquema junto a la ruta.
  • Compón el middleware con createMiddleware y delimítalo mediante un patrón de ruta.
  • Exporta AppType y utiliza hc en el cliente en lugar de escribir los tipos a mano.
  • Mantén los handlers ligeros; mueve la lógica reutilizable a funciones simples o servicios.
  • Devuelve los códigos de estado correctos con c.json(body, status) en lugar de enviar siempre un 200.
  • Diseña pensando en el runtime: sin sistema de archivos, CPU limitada y almacenamiento edge-native.
  • Prueba con app.request() para que los tests no necesiten servidor ni red.
test("GET /posts", async () => {
  const res = await app.request("/posts");
  expect(res.status).toBe(200);
});

Errores comunes

  • Asumir que las librerías de Node funcionan en Workers; los módulos integrados y los complementos nativos a menudo no lo hacen.
  • Mantener conexiones a la base de datos abiertas en un edge runtime en lugar de utilizar HTTP o bindings.
  • Olvidar retornar la respuesta, haciendo que el handler se resuelva a undefined.
  • Registrar cors después de las rutas a las que debería aplicarse.
  • Leer c.req.json() más de una vez, lo cual falla porque el stream del cuerpo ya ha sido consumido.
  • Omitir la validación y confiar en strings de c.req.query().
  • Permitir que AppType se desincronice al escribir manualmente los tipos del cliente en lugar de importarlos.
  • Realizar tareas pesadas de CPU durante una solicitud y alcanzar el límite de tiempo del runtime.

Próximos pasos

Hono lleva el modelo de routing y middleware que ya conoces de Express a runtimes que ni siquiera existían cuando se escribió Express. Si necesitas un servidor de Node de larga duración con validación basada en esquemas, compáralo con Fastify. Para entender los runtimes a los que se dirige Hono, lee la guía de conceptos básicos de Node.js, y cuando estés listo para lanzar a producción, el roadmap de backend cubre el despliegue en la nube. Después, construye una API pequeña, despliégala en Workers y llámala utilizando un cliente RPC tipado.

En la practica

Rutas, middleware, validación y RPC

Un servicio de Hono desde cuatro ángulos: un router, un middleware reutilizable, un handler validado y un cliente tipado.

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;

Compromisos

¿Es Hono el framework adecuado para ti?

Hono está optimizado para estándares, portabilidad y cold starts. Este enfoque es ideal en el edge y menos relevante en un servidor Node de larga duración.

Strengths

  • Verdaderamente portable

    El mismo código se ejecuta sin cambios en Workers, Deno, Bun, Node y varias plataformas, manteniendo tus opciones abiertas a medida que evoluciona el hosting.

  • Diminuto y rápido

    El núcleo ocupa unos pocos kilobytes sin dependencias integradas de Node, por lo que se empaqueta limpiamente y arranca al instante.

  • Tipos sin codegen

    El modo RPC otorga al cliente conocimiento total de las rutas y respuestas directamente desde los tipos del servidor, detectando desvíos en la API en tiempo de compilación.

Trade-offs

  • El ecosistema es más joven

    Hono tiene una librería de middleware creciente, pero nada comparado con las décadas de paquetes de Express. Tendrás que escribir más código de integración tú mismo.

  • El almacenamiento en el edge es diferente

    Workers no tienen sistema de archivos local y el tiempo de CPU es limitado. Debes diseñar basándote en KV, D1 o R2 en lugar de una conexión de base de datos tradicional.

  • No todas las librerías de Node funcionan

    El código que depende de módulos integrados de Node puede no funcionar en el runtime de Workers. En Node vía el adaptador está bien, pero la portabilidad no es automática.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Hono?

Nuestro tutorial interactivo te guia a traves de Hono paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.