Un framework web "ligero" de Node.js —Hono, Koa, Polka y otros similares— se trata menos del tamaño del archivo que de una base diferente: en lugar de envolver los objetos http.IncomingMessage/http.ServerResponse propios de Node, estos frameworks se construyen sobre las clases estándar webRequest y Response, las mismas que ya entienden un navegador o un Cloudflare Worker.
Esa única elección se extiende a casi todo lo demás que cubre esta sección.
Es la razón por la que el mismo manejador de rutas puede ejecutarse en Node, en el edge o en Bun con pocos o ningún cambio, por qué el middleware se compone de la manera en que lo hace y por qué estos frameworks tienden a venir con una fracción del árbol de dependencias de Express.
Conceptos básicos de Hono y Koa y Polka muestran el código; esta página es el modelo subyacente: lo que "ligero" realmente te ofrece y lo que cuesta.
Los frameworks ligeros reemplazan los objetos de solicitud/respuesta específicos de Node con la API estándar Request/Response de la web, lo que los hace portátiles entre tiempos de ejecución y mantiene su núcleo pequeño.
Por qué es importante: El diseño de Express es anterior a los Estándares Web, por lo que está ligado al módulo http de Node; ese vínculo es invisible hasta que intentas ejecutar el mismo manejador en un lugar que no sea Node, o hasta que el peso de las dependencias comienza a importar para los arranques en frío.
Conceptos clave:API de Estándares Web, composición de middleware (el modelo de cebolla), capa adaptadora, arranque en frío, huella.
Cuándo usarlo: Desplegar la misma lógica de API tanto en un servidor Node como en un entorno de ejecución edge/serverless, minimizar la latencia de arranque en frío, construir un servicio con una superficie de dependencia genuinamente pequeña o simplemente querer primitivas de enrutador/middleware sin un gran ecosistema adjunto.
Limitaciones / Compensaciones: Ecosistemas más pequeños significan menos paquetes de middleware probados y respuestas de la comunidad; algunas API solo de Node (sockets sin procesar, ciertos patrones de streaming) aún necesitan una vía de escape de la forma estándar Request/Response.
Temas relacionados: Módulo http de Node, la API Fetch, entornos de ejecución edge/serverless, tuberías de middleware, compensaciones en la selección de frameworks.
Todo framework web de Node tiene que responder primero a una pregunta: ¿cómo se ve una solicitud dentro de un manejador?
Express, Koa (en su forma clásica) y el módulo http sin procesar responden con los objetos nativos de Node: IncomingMessage para la solicitud, ServerResponse para la respuesta, objetos basados en streams que existen solo porque el módulo http de Node es anterior a cualquier estándar web para representar un intercambio HTTP en JavaScript.
Hono responde de manera diferente: un manejador recibe un objeto Request estándar y devuelve un objeto Response estándar, las mismas clases exactas definidas por la API Fetch y ya implementadas en todos los navegadores modernos, en Deno, en Bun y en entornos de ejecución edge como Cloudflare Workers.
Node mismo ha soportado estas clases globales de forma nativa desde la versión 18, por lo que una aplicación Hono que se ejecuta bajo @hono/node-server no está simulando una API de navegador, está usando la misma que el tiempo de ejecución ya proporciona.
Una analogía útil: piensa en Request/Response como un estándar de contenedor de envío.
Antes de los contenedores estandarizados, cada puerto necesitaba su propio equipo de carga especializado para cada tipo de carga; una vez que todos acordaron la forma del contenedor, cualquier grúa en cualquier puerto podía mover cualquier carga, porque el equipo solo necesitaba entender el contenedor, no lo que había dentro.
http.IncomingMessage de Node es una carga con forma para un muelle específico; Request/Response de la API Fetch es el contenedor de envío, y un framework construido alrededor del contenedor, no del muelle, puede moverse a cualquier puerto que también hable contenedores.
// Manejador de Hono: una función simple de un contexto tipo Request a Responseapp.get("/users/:id", (c) => { return c.json({ id: c.req.param("id") }); // c.json() devuelve un Response estándar});
Koa se encuentra en el medio: todavía envuelve los objetos http nativos de Node, pero reemplazó el middleware basado en callbacks de Express con async/await y un único objeto ctx temprano, razón por la cual se agrupa con frameworks ligeros aunque no esté basado en Estándares Web como Hono.
Polka va en la dirección opuesta por completo: mantiene los objetos nativos de Node y simplemente agrega un enrutador rápido y mínimo encima, sacrificando la portabilidad por la huella más pequeña posible específicamente en Node.
La parte de "ligero" que realmente cambia la forma en que escribes código es la composición de middleware, y funciona de la misma manera en Hono, Koa y Express, aunque los objetos subyacentes difieran: el middleware no se ejecuta en una lista plana y secuencial.
Se anida, como capas de una cebolla, porque cada función de middleware recibe una callback next() y decide cuándo, o si, llamarla.
solicitud entrante
├─ middleware de logger (trabajo antes de next())
│ ├─ middleware de auth (trabajo antes de next())
│ │ ├─ manejador de ruta (capa más interna)
│ │ └─ auth: trabajo después de que next() retorna (ej. encabezado de respuesta)
│ └─ logger: trabajo después de que next() retorna (ej. duración del registro)
└─ respuesta saliente
El código registrado antes de await next() se ejecuta al entrar, en orden de registro; el código registrado después de await next() se ejecuta al salir, en orden inverso, razón por la cual un middleware de registro que mide el tiempo de una solicitud tiene que envolver la llamada en next(), no solo registrar una vez al principio.
Este modelo de cebolla es lo que hace que el middleware sea componible: una verificación de autenticación puede cortocircuitar al no llamar nunca a next(), y un middleware de modelado de respuesta puede inspeccionar o reescribir lo que produjo el manejador antes de que llegue al cliente, porque todavía está "dentro" de la llamada cuando next() retorna.
Bajo el capó, la elección de los Estándares Web también cambia cómo una solicitud llega a esa cadena de middleware en primer lugar.
El núcleo de una aplicación Hono no tiene idea de que se está ejecutando en Node en absoluto: @hono/node-server es un adaptador, una fina capa de traducción que escucha en un http.Server real de Node, convierte cada IncomingMessage entrante en un Request estándar, lo entrega a la aplicación Hono y convierte el Response que regresa en lo que necesite el socket de Node.
Ese adaptador es el único código específico de Node en toda la pila; cámbialo por un adaptador de Cloudflare Workers (integrado en la plataforma, no se necesita traducción) o un adaptador de Bun, y el mismo objeto app se ejecuta en otro lugar, sin modificar, porque nunca tocó los objetos de Node para empezar.
"Ligero" es en realidad una abreviatura de dos propiedades separadas que no siempre van juntas: huella pequeña (menos dependencias, menor tamaño de instalación, menos código para analizar y compilar JIT al inicio) y portabilidad en tiempo de ejecución (funciona fuera de Node sin una reescritura).
Polka tiene la primera propiedad sin la segunda: es diminuto, pero está diseñado solo para Node.
Hono tiene ambas, porque la portabilidad es lo que permitió a sus autores mantener el núcleo libre de código específico de Node en primer lugar; un framework que busca los globales Buffer o process de Node dentro de su núcleo no puede ejecutarse sin modificar en un entorno de ejecución edge, por lo que evitarlos es una restricción que también reduce el árbol de dependencias.
La huella se justifica principalmente en el arranque en frío: en una plataforma serverless o edge, una nueva instancia tiene que cargar e inicializar el framework antes de poder manejar su primera solicitud, y cada megabyte de código de dependencia y cada cadena require() síncrona se suma a esa latencia.
Una vez que un proceso de Node está caliente y funcionando de manera constante, sin embargo, la brecha entre Hono y Express se reduce drásticamente: ambos están limitados por E/S por el mismo bucle de eventos, y la sobrecarga propia de ninguno de los frameworks suele ser el cuello de botella en comparación con las llamadas a la base de datos o la serialización.
Esa es la compensación honesta: elige un framework ligero por su portabilidad o por implementaciones sensibles al arranque en frío, no bajo la suposición de que hará que un servicio Node ya caliente sea drásticamente más rápido.
La brecha del ecosistema es el otro costo real: Express tiene más de quince años de middleware, respuestas de Stack Overflow y guías de integración que la comunidad de un framework más pequeño aún no ha acumulado, por lo que los equipos a veces terminan escribiendo pequeños adaptadores o middleware ellos mismos que habrían sido una línea de npm install en Express.
Enfoque
Fortaleza
Debilidad
Mejor ajuste
Hono (núcleo de Estándares Web)
Se ejecuta sin modificar en Node, Workers, Deno, Bun; núcleo diminuto
"Ligero solo significa menos paquetes npm." Menos dependencias es un síntoma, no la causa; el verdadero motor es si el núcleo del framework depende de objetos solo de Node, lo que determina tanto la huella como la portabilidad.
"Una aplicación Hono es básicamente una aplicación de navegador ejecutándose en el servidor." Reutiliza las mismas clasesRequest/Response que define el navegador, pero aún ejecuta código del lado del servidor con acceso completo a la API de Node a través de su capa adaptadora; no está en un entorno aislado como el JavaScript del navegador.
"Los frameworks más pequeños siempre son más rápidos." Suelen ganar en el arranque en frío; una vez que un proceso está caliente, la sobrecarga del framework rara vez es el costo dominante en comparación con la base de datos o la E/S de red.
"Koa es básicamente Express con una sintaxis diferente." El núcleo de Koa se envía deliberadamente sin enrutamiento ni análisis de cuerpo incorporados, confiando completamente en el middleware para eso; un núcleo más pequeño y componible que el de Express, aunque ambos usen los objetos http nativos de Node.
"El middleware siempre se ejecuta de arriba a abajo, una vez." Cada función de middleware envuelve todo lo que viene después; el código puede ejecutarse de nuevo después de que next() retorne, al salir, razón por la cual el orden cambia el comportamiento en ambas direcciones.
¿Qué hace que un framework sea realmente "ligero" en lugar de simplemente tener un paquete más pequeño?
Su núcleo evita depender de objetos específicos de Node (como http.IncomingMessage) y globales solo de Node, construyéndose en su lugar sobre las clases estándar Request/Response. Eso es lo que lo hace pequeño y portátil; las dos propiedades suelen ir juntas, pero no son lo mismo.
¿Por qué los manejadores de Hono se ven tan diferentes de los manejadores de Express?
Un manejador de Express recibe los objetos nativos req/res de Node y muta res directamente; un manejador de Hono recibe un contexto que envuelve un Request estándar y devuelve un objeto Response estándar, coincidiendo con la misma API que usa una llamada Fetch o un Cloudflare Worker.
¿"Estándares Web" significa que Hono ejecuta el mismo código que un navegador?
No, significa que el núcleo de Hono reutiliza las mismas clases Request/Response que define un navegador, por lo que las formas coinciden, pero el código aún se ejecuta del lado del servidor con acceso completo al tiempo de ejecución a través del adaptador (Node, Workers, Bun) que lo esté ejecutando.
¿Cómo afecta realmente el orden del middleware al comportamiento?
Cada middleware envuelve todo lo registrado después de él, por lo que el código antes de await next() se ejecuta al entrar, en orden, y el código después de next() se ejecuta al salir, en orden inverso; mover un middleware antes o después cambia tanto cuándo ve la solicitud como cuándo ve la respuesta.
¿Qué es un adaptador y por qué Hono necesita uno para Node?
Un adaptador (@hono/node-server para Node) es la capa de traducción entre los objetos nativos de un tiempo de ejecución y el núcleo de Estándares Web del framework; convierte un IncomingMessage entrante en un Request y convierte el Response devuelto en lo que necesita el socket de Node. Es el único código específico de Node en la pila.
¿Es Polka "ligero" en el mismo sentido que Hono?
Solo en el eje de la huella: Polka es solo de Node y no utiliza la forma Request/Response de los Estándares Web, por lo que es pequeño pero no portátil a entornos de ejecución edge o no-Node como Hono.
¿Cambiar a un framework ligero hará que mi API sea más rápida?
Principalmente en el arranque en frío, donde menos código para cargar e inicializar es lo más importante. Una vez que un proceso está caliente, la sobrecarga del framework rara vez domina en comparación con las llamadas a la base de datos, la serialización y la E/S de red.
¿Por qué Koa se considera "ligero" si todavía es solo de Node?
Porque su núcleo es inusualmente mínimo para un framework nativo de Node (sin enrutador ni analizador de cuerpo incorporados, middleware async/await primero y una pequeña huella de dependencias), aunque no comparte la portabilidad edge de Hono.
¿Cuál es el costo real de elegir un ecosistema de framework más pequeño?
Menos paquetes de middleware preconstruidos y mantenidos por la comunidad, y menos respuestas existentes a problemas comunes; los equipos a veces escriben pequeñas integraciones ellos mismos que ya existirían para Express.
¿Puede el código de Node que usa `Buffer` o `process` seguir ejecutándose dentro de un manejador de Hono?
Sí, en Node; el adaptador proporciona a los manejadores acceso completo al tiempo de ejecución de Node. La restricción que mantiene a Hono portátil es que su propio núcleo evita depender de esos globales, no que el código de tu aplicación no pueda usarlos cuando se ejecuta específicamente en Node.
¿Tengo que elegir un framework para cada servicio de mi sistema?
No, es común usar Express o Fastify para un monolito solo de Node y Hono específicamente para servicios que necesitan ejecutarse en una plataforma edge o ser portátiles, eligiendo por servicio según dónde se despliegue realmente ese servicio.
¿Usar `Request`/`Response` estándar significa que pierdo el acceso a las respuestas de streaming?
No, el constructor Response estándar acepta un cuerpo ReadableStream, por lo que el streaming funciona de la misma manera conceptual que con los streams de Node, solo que se expresa a través de la API Web Streams en lugar de las clases de stream de Node.