Hono: Framework para aplicaciones web

Cuando me enfrento a un nuevo proyecto, ya sea una API para una aplicación móvil o un backend para un sitio web, siempre busco herramientas que me hagan la vida más simple y fácil. No quiero mantener configuraciones complejas ni enfocar esfuerzos a optimizar código con un framework pesado que consuma más recursos de los necesarios. Mis prioridades son simples: quiero que sea rápido, ligero y que se integre sin problemas con Deno, que es el entorno de ejecución de TypeScript que he adoptado para casi todo.

Y ahí es donde entra Hono.

Descubrí Hono buscando una alternativa moderna a Express.js (un framework que usé durante años en el mundo de Node.js). Aunque Express es casi de facto el framework web para node.js, sentía que se estaba quedando desactualizado no encajaba del todo con la simplicidad y seguridad que me ofrecía Deno. Necesitaba algo que se sintiera más nativo, más ágil y moderno.

Lo que me convenció de Hono fue precisamente eso. Es increíblemente liviano, casi no añade overhead a mi aplicación, y su rendimiento es espectacular. Pero la verdadera joya de la corona, para mí, es su versatilidad. Aunque mi ecosistema principal es Deno, Hono me da la tranquilidad de saber que mi código es portable. Si el día de mañana decido mover una API a Cloudflare Workers para que corra en el edge (más cerca de los usuarios), o si un cliente me pide desplegarla en un servidor tradicional con Node.js, puedo hacerlo con cambios mínimos. Esa libertad es algo que valoro muchísimo.

Como vengo del mundo de Express, empezar con Hono fue muy sencillo. La forma de definir rutas, gestionar peticiones (req) y respuestas (res) y usar middlewares es tan intuitiva y familiar que no sentí que hiciera un esfuerzo adicional, es decir, si vienes del mundo Express, la curva de aprendizaje es rápida.

En este post quiero contarte, desde mi experiencia, por qué Hono se ha convertido en mi framework de cabecera para casi cualquier proyecto web que involucre un backend.

Manos a la Obra: Tu Primer Proyecto con Hono y Deno

Ya te conté por qué me gusta Hono, ahora vamos a la práctica para que veas lo rápido que es empezar.

Primero, abre tu terminal, crea una carpeta para el proyecto y entra en ella:

1
mkdir mi-proyecto-hono && cd mi-proyecto-hono

Deno tiene un comando muy práctico para inicializar proyectos que nos ahorra bastante trabajo. Usaremos el siguiente:

1
deno init

Este comando te creará una estructura básica con un main.ts y un deno.json. Ahora, para instalar Hono, agregamos la dependencia en el archivo deno.json y luego simplemente lo importamos en nuestro main.ts. Deno lo descargará y cacheará la primera vez que ejecutes el código.

Puedes agregar Hono como dependencia usando JSR de la siguiente forma, pero debes editar el archivo deno.json para corregir el alias en el import.

1
deno add jsr:@hono/hono

El corazón de tu proyecto - deno.json

Antes de ver el código, echemos un vistazo al deno.json. Aquí es donde Deno gestiona las dependencias y tareas. Para usar Hono, podemos añadirlo a la sección imports o usar la línea de comandos de deno para agregar la dependencia:

1
2
3
4
5
6
7
8
{
"imports": {
"hono": "jsr:@hono/hono@^4.10.3" // <-- ojo con el alias
},
"tasks": {
"start": "deno run --allow-net main.ts"
}
}

Un detalle importante es que, como ves, Hono está disponible en JSR (el nuevo registro de paquetes de JavaScript), lo que hace que manejar las versiones sea muy limpio.

Primer controlador

Ahora sí, el código de main.ts. Es tan simple y elegante como esto:

1
2
3
4
5
6
7
8
9
import { Hono } from "hono";

const app = new Hono();

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

Deno.serve(app.fetch);

¿Qué hace este código?

  1. Importa Hono.
  2. Crea una nueva instancia de la aplicación.
  3. Define una ruta para el método GET en la URL raíz (/).
  4. Cuando alguien visita esa ruta, le devuelve el texto “Hello Hono!”.
  5. Finalmente, Deno.serve inicia el servidor.

Para ponerlo en marcha, usamos la tarea que definimos en deno.json:

1
deno task start

Como Deno es seguro por defecto, te pedirá permiso para acceder a la red. ¡Y listo! Ya tienes un servidor corriendo. Si usas una herramienta como httpie, verás esto:

1
2
3
4
5
6
$ http :8000/

HTTP/1.1 200 OK
...

Hello Hono!

¿Y si quiero devolver JSON?

Fácil. Hono tiene un método específico para eso. Simplemente cambia c.text() por c.json():

1
2
3
app.get("/", (c) => {
return c.json({ message: "Hello Hono!" });
});

El resultado ahora será una respuesta JSON perfecta:

1
2
3
4
5
6
7
8
9
$ http :8000/

HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
...

{
"message": "Hello Hono!"
}

En solo unos minutos, hemos montado un servidor web funcional. Esta simplicidad y rapidez es lo que me terminó de convencer de la combinación de Hono y Deno.

¿ Cómo cambiar el puerto del servidor ?

Esto me costó pillarlo en su momento, así que dejo aquí cómo se hace:

1
Deno.serve({ port: 8080 }, app.fetch);

Revisemos algunos middlewares útiles

logger

Un middleware bien útil es el de logger, permite registrar (o loggear) lo que entra y lo que sale a Hono (obviamente depende de la ubicación en el archivo principal de la aplicación.)

1
2
3
4
5
6
import { Hono } from "hono";
import { logger } from "hono/logger";

const app = new Hono();

app.use(logger());

Con esta configuración se puede obtener este tipo de logs.

1
2
3
Listening on http://0.0.0.0:8000/ (http://localhost:8000/)
<-- GET /
--> GET / 200 0ms

etag

La cabecera o header ETag (que viene de Entity Tag o “etiqueta de entidad”) es básicamente una huella digital o un identificador único que el servidor le asigna a una versión específica de un recurso (como una página, una imagen o un archivo JSON).

Imagina que tienes un archivo en tu servidor. Cada vez que modificas y guardas ese archivo, su ETag cambia. Es como el número de versión de ese recurso en un momento exacto.

¿Y para qué sirve?

Su propósito principal es hacer que el cacheo sea mucho más eficiente y ahorrar ancho de banda. El flujo es el siguiente:

  1. Primera Visita: Cuando tu navegador pide un recurso (ej: styles.css) por primera vez, el servidor se lo envía completo y, además, incluye el header ETag con su “huella digital”. Por ejemplo: ETag: “v1-a2b3c4d5”.
  2. El Navegador Guarda la Huella: Tu navegador guarda el archivo styles.css en su caché y también anota su ETag.
  3. Siguientes Visitas: La próxima vez que necesite ese mismo archivo, el navegador no lo pide a ciegas. En su lugar, le pregunta al servidor: “Oye, tengo una versión de styles.css con la huella v1-a2b3c4d5. ¿Sigue siendo la buena?”. Esto lo hace enviando un header llamado If-None-Match: “v1-a2b3c4d5”.
  4. El Servidor Decide:
    • Si la “huella digital” del archivo en el servidor coincide con la que envió el navegador, significa que el archivo no ha cambiado. El servidor responde con un 304 Not Modified. Este mensaje es muy liviano, no contiene el archivo, y básicamente le dice al navegador: “Todo sigue igual, usa la copia que ya tienes en tu caché”.
    • Si la “huella digital” no coincide, significa que el archivo fue actualizado. El servidor responde con un 200 OK, envía la nueva versión del archivo y, por supuesto, su nuevo ETag.

En resumen, el ETag permite al navegador evitar volver a descargar recursos que no han cambiado, haciendo que la navegación sea mucho más rápida y consuma menos datos. Además ayuda a las capas superiores de la aplicación a tomar mejores decisiones respecto del cache de la aplicación.

Ahora la configuración para Hono:

1
2
3
4
5
6
import { Hono } from "hono";
import { etag } from "hono/etag";

const app = new Hono();

app.use(etag());

Finalmente esta será la respuesta donde aparece una cabecera con el etag.

1
2
3
4
5
6
7
8
9
10
11
http :8000/
HTTP/1.1 200 OK
content-length: 25
content-type: application/json
date: Tue, 28 Oct 2025 03:25:31 GMT
etag: "6e488564c25da2b4d7326d5ebfa92e957af61dcb"
vary: Accept-Encoding

{
"message": "Hello Hono!"
}

cors

Probablemente, uno de los “problemas” más comunes al empezar a desarrollar una API es toparse con un error de CORS en el navegador.

Imagina que los sitios web son como reinos amurallados. Por seguridad, un reino (tudominio.com) no puede simplemente enviar un mensajero a pedirle recursos a otro reino (api.otrodominio.com). Esta regla se llama Política del Mismo Origen (Same-Origin Policy) y es fundamental para la seguridad en la web.

El middleware de CORS de Hono es el pasaporte diplomático que nos permite configurar qué “reinos” externos tienen permiso para comunicarse con nuestra API.

Por ejemplo, una configuración de CORS muy específica y segura podría ser así:

1
2
3
4
5
6
7
8
9
10
11
import { Hono } from 'hono';
import { cors } from 'hono/cors';

app.use('/api/*', cors({
origin: 'http://example.com',
allowMethods: ['POST', 'GET', 'OPTIONS'],
allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
maxAge: 600,
credentials: true,
}));

Analicemos las reglas que hemos definido en este bloque:

  • app.use('/api/*', ...): Primero, le indicamos a Hono que aplique este middleware únicamente a las rutas que comiencen con /api/. Esto es muy práctico para tener políticas de seguridad distintas para diferentes partes de tu aplicación.
  • origin: 'http://example.com': La regla más importante. Se especifica que solo las peticiones que vengan desde http://example.com están permitidas.
  • allowMethods: [...]: Define qué métodos HTTP están autorizados (POST, GET, OPTIONS). Un DELETE sería bloqueado.
  • allowHeaders: [...]: Una lista blanca de las cabeceras HTTP que el cliente tiene permitido enviar.
  • exposeHeaders: [...]: Permite que el código JavaScript del cliente pueda leer cabeceras de la respuesta que, por defecto, el navegador oculta por seguridad, como Content-Length.
  • maxAge: 600: Una optimización de rendimiento. Indica al navegador que puede guardar en caché el resultado de la petición de “verificación” (preflight) durante 600 segundos (10 minutos).
  • credentials: true: Esencial si tu frontend necesita enviar credenciales como cookies o cabeceras de autenticación (Authorization).

En resumen, este middleware te da un control muy granular para configurar una política de CORS segura, una práctica mucho más recomendable que simplemente abrir el acceso a todo el mundo con origin: '*'.

Palabras al Cierre

Como hemos visto en este recorrido, Hono se presenta como una alternativa muy atractiva en el ecosistema de JavaScript y TypeScript. Su filosofía minimalista, su rendimiento y, sobre todo, su capacidad para adaptarse a cualquier entorno de ejecución, lo convierten en una herramienta que vale la pena tener en nuestro arsenal.

Personalmente, la combinación de la simplicidad de Hono con la seguridad y el entorno moderno de Deno ha hecho que desarrollar APIs vuelva a ser una experiencia ágil y productiva.

Pero esto es solo el comienzo. Este artículo es el primero de una serie donde exploraremos a fondo el ecosistema de Deno y Hono. En las próximas entregas, veremos temas un poco más avanzados como:

  • Creación de nuestros propios middlewares.
  • Helpers de Hono
  • Rutas avanzadas y manejo de grupos.
  • Estrategias de testing para nuestras APIs.

Te invito a que experimentes con lo que hemos visto hoy y, por supuesto, a que te mantengas atento para los próximos artículos de la serie.


Referencias Útiles

Aquí te dejo una lista de enlaces que te serán de gran ayuda para profundizar en Hono y Deno:

Programando como matar un dragón

Java - Llega, encuentra al dragón, desarrolla un framework para aniquilación de dragones en múltiples capas, escribe varios artículos sobre el framework… pero no mata al dragón.

.NET - Llega, ve la idea del desarrollador de Java y la copia, intenta matar al dragón, pero el bicho se lo come

C - Llega, mira al dragón con mirada de desprecio, tira de espada, degolla al dragón, encuentra a la princesa… y la ignora para ver los últimos checkins del cvs del kernel de linux

C++ - Crea un pincho básico y va juntando funcionalidades hasta tener una espada compleja que apenas consigue entender… mata al dragón pero se atasca en medio del puente por culpa de pérdidas de memoria (memory leaks)

COBOL - Llega, ve al dragón y piensa que es demasiado viejo para conseguir matar un bicho de ese tamaño y quedarse con la princesa, y entonces se va

Pascal - Se prepara durante 10 años para crear un sistema de aniquilación de dragones… cuando llega el momento descubre que el programa sólo acepta lagartijas como entrada

VB - Monta un arma de destrucción de dragones a partir de varios componentes, salta encima del lomo del dragón, y en la hora H descubre que la espada sólo funciona durante las noches de lluvia…

PL/SQL - Recoge datos de otros matadores de dragones, crea tablas con n relaciones de complejidad ternaria, datos en tres dimensiones, OLAP, tarda quince años para procesar la información… y para entonces la princesa se volvió lesbiana.

Ruby - Llega con muchísima fama, diciendo que es el mejor en hacer cualquier cosa y cuando va a enfrentarse al dragón muestra una peliculita en la que él mismo aparece matando a un dragón… el dragón se lo come de puro aburrimiento

Smalltalk - Llega, analiza al dragón y a la princesa, se da la vuelta y se pira: ellos son muy inferiores

shell - Crea un arma poderosa para matar dragones, pero en la hora H no recuerda como usarla

shell(2)- El tío se acerca al dragón con un script de dos líneas que mata, corta, destripa, empala, pica en pedacitos y empaca al bicho, pero a la hora de ejecutarlo el script aumenta, engorda, enfurece y pone alcohol en el fuego del dragón.

Ensamblador - Cree que está haciendo lo más correcto y eficiente… pero pone un A en lugar de un D y mata a la princesa para terminar follándose al dragón

Fortran - Llega y desarrolla una solución con 45 mil líneas de código, mata al dragón, va al encuentro de la princesa… pero ella le llama tirillas y se va corriendo detrás del programador de java que era elegante y además es rico

FOX PRO - Desarrolla un sistema para matar al dragón. Por fuera es precioso y funciona, pero por dentro está todo parcheado y cuando va a ejecutar el aniquilador de dragones recuerda que olvidó indexar los DBF.

ANALISTA DE PROCESOS - Se acerca al dragón con dos toneladas de documentación desarrollada sobre el proceso de matar un dragón genérico, desarrolla un DFD para liberar a la princesa y casarse con ella, convence al dragón de que es lo mejor para el y que no va a doler. Al ejecutar el proceso estima el esfuerzo y el tamaño del daño que causará con la firma del papa, de Buda y de Joan Manuel Serrat para el plano, y entonces compra dos bombas nucleares, 45 cañones, un portaaviones y contrata a 300 hombres armados hasta los dientes… cuando en realidad tan sólo necesitaría la espada que tenía en la mano desde el principio

CLIPPER: Monta una rutina que carga un array de codeblocks para insultar al dragón, cantarle a la princesa, cargar la espada a memoria, moler al dragón, limpiar la suciedad, preparar un vaso de leche condensada con moras para la princesa, follar a la princesa, darse un baño, encender el coche, ponerle gasolina y volver para casa. A la hora de ejecutar recibe un “Bound Error: Array Access” y el dragón se lo come con patatas

Lisp, donde el famoso caballero andante, tras hablar con numerosos expertos en matar dragones y modelar el conocimiento que ellos poseen programa el sistema y se da cuenta… de que se ha dejado algún paréntesis.

HTML: Monta una web sobre espadas famosas usadas para matar dragones, pero se pasa los estándares W3C por el forro. Cuando se encara con el dragón descubre que el código no es compatible con su navegador, por lo que se queda compuesto y sin espada. El dragón se lo merienda como aperitivo.

Prolog: Piensa que para matar al dragón necesita un arma. Busca en un catálogo 182014 armas. Para cuando la princesa muere de vieja ya ha logrado descubrir como fabricar todas las armas que empiezan por la A: Armas atómicas, Alabardas,Alfanges, Asesinos contratados, Armas blancas, Antiaéreos, Arcos, …

PHP: Crea una página web que al ejecutarla eliminará al $dragón tirando de una base de datos de armas en mysql y sobre un servidor apache. Sin embargo, se olvidó el Where en la query de delete y mata a la princesa, al dragon, a los campesinos, a la bruja, al hechicero y al propio programador.

JavaScript: El programador intenta matar al gran dragón verde que lanza fuego por la boca. Crean un script que borrará al dragón cuando carge una página web para unos segundos después crear unas damiselas que lancen flores y hagan soniditos de aplausos. Por desgracia no tuvo en cuenta la estructura Dom del lagarto, también conocido como Mozilla, y lo único que consigue es rellenar su consola de errores y que el libro de mozilla narre como acabó devorado.

ActiveX: Los programadores crean un tunel para entrar a la guarida del dragón desde el castillo y ejecutar un programa que matará al dragón desde una distacia segura y prudencial. El dragón descubre el tunel, se come a los trabajadores que cavaban, a los matadores de dragones y esclaviza a todos los siervos del castillo que pasan a ser sus esclavos. El castillo pasa a ser un lugar de cría de dragones lleno crías que manda en pop ups a otros castillos. Los restos poco apetitosos de los caballeros los mete en latas de Spam y manda también a otros castillos como advertencia.

Basic: Crean un arma capaz de matar a dragones de papel, pero mucho que la perfeccionan descubren que no sirve para matar a ningún dragón más grande que una cría de caniche.

Matlab: Crean un bucle que calcula las trayectorias para lanzar una flecha gigante contra el dragón. El programa funciona perfectamente. Sólo faltan los voluntarios capaces de lanzar la flecha con la fuerza y puntería necesaria.

Programador de videojuegos:Se pasa dos años programando una espada state of the art, con shaders y todo. A la hora de matar al dragón se encuentra con que la mitad de los caballeros no tienen fuerza para mover la espada. Luego alguien programa un parche que revela las escenas de sexo con la princesa y Hillary Clinton le monta un escándalo.

Perl - El caballero decide matar al dragón con una expresión regular, pero se equivoca en los carácteres de comodín y acaba incluyendo en el patrón de mortalidad a Dragones, Iguanas, lagartos, perros, gatos, osos, princesas y ratones.

HyperCard: crea en 5 minutos una pila con un catálogo de armas, con fotos, gráficas y vídeos sobre su utilización y los distintos tipos de dragones que puede matar, y que además fabrica el arma elegida utilizando las herramientas de dibujo, con una interfaz impecable y fantásticos efectos visuales, lo guarda como ejecutable, le pone un bonito icono, pero cuando va a fabricar la espada no funciona porque se dejó un XCMD en casa (Home).

Macromedia Director: crea una mágnifica interfaz destellante mata dragones, con una espada deslumbrante, fabricada a bases de Xtras de terceros fabricantes, al primer intento de matar al dragon “Script Error”, entonces a duras penas se esquiva el mordisco, y se procede al segundo intento… pero el ejecutable va tan lento que se los come a todos.

Mathematica: se crea y modela el objeto logico dragon. se modela y crea igualmente la princesa, la espada, al principe. se modela el caso especial de la articulacion manoespada, y la discontinuidad piel de dragonespada. Cuando todo esta hecho, se le pide a Matematica que lo resuelva, y el resultado es: “Hay que matar al dragon con la espada, y quedarse con la princesa”

Escuchando Primus - “Mama Didn’t Raise No Fool”