Dentro de DenoX: Enrutamiento de archivos, rebanadas MVC y un contrato AGENTS.md en Deno
Cómo el framework DenoX combina Hono, enrutamiento basado en archivos, segmentos de funcionalidades, middleware de seguridad global y un flujo de trabajo AGENTS.md centrado en las especificaciones para agentes de programación por IA.
Conectar un servidor rara vez es la parte más importante de un proyecto backend; lo que realmente importa es lanzar funcionalidades. Rails, Laravel y Next.js ganaron a los desarrolladores al tomar por ellos las decisiones técnicas necesarias, y Deno, con sus permisos seguros por defecto, TypeScript nativo e herramientas integradas, es un candidato ideal para recibir el mismo trato. DenoX es un framework full-stack de código abierto para Deno, desarrollado sobre Hono, que busca ser precisamente esa capa con opiniones definidas. Sus respuestas a preguntas estructurales recurrentes, y la forma en que las documenta para agentes de codificación basados en IA, son patrones que se pueden reutilizar en cualquier backend desarrollado con TypeScript.
El vacío que llena una capa con opiniones definidas
Deno 2 introdujo compatibilidad con npm, el registro JSR, una biblioteca estándar madura y un único binario capaz de realizar análisis de código, formato, pruebas, compilación y empaquetado (véase cómo Deno 2.x resolvió los problemas de compatibilidad con Node y la fatiga por herramientas para más detalles). Lo que un entorno de ejecución básico no puede ofrecer es un consenso sobre las cuestiones que discute cada equipo: dónde debe ir la lógica de negocio, cómo se deben reportar los errores, quién valida la configuración y dónde se aplica el límite de frecuencia.
DenoX responde a estas preguntas con un único principio: la convención sobre la configuración, verificada mediante herramientas. Las convenciones documentadas pueden cambiar; las que se comprueban en los procesos de integración continúa manteniéndose.
Ruteo basado en archivos con una tabla generada y guardada
Las rutas provienen del sistema de archivos. Al agregar un archivo en la carpeta pages se crea una URL, donde los segmentos entre corchetes se convierten en parámetros:
src/frontend/pages/
├── index.ts → /
├── about/main.ts → /about
├── users/main.ts → /users
└── posts/[id].ts → /posts/:id
El descubrimiento no se realiza en tiempo de ejecución. Al ejecutar deno task routes se recorre el árbol y se genera una tabla de rutas estática y determinista. Dos aspectos hacen que esto sea robusto:
- Las rutas estáticas siempre se registran antes que las dinámicas, por lo que
/users/newnunca podrá ser absorbido por/users/:id. En routers de primera coincidencia como Hono, el orden de registro determina el comportamiento, y su generación elimina una fuente común de errores sutiles. - El archivo generado se guarda en el repositorio, y las pruebas continuas fallan si está desactualizado.
Las páginas son funciones simples
Una página es un módulo ordinario de TypeScript. Importa el tipo Context de Hono y una herramienta para escapar HTML:
import type { Context } from "hono";
import { escapeHtml } from "@/shared/html.ts";
Luego exporta un objeto config que selecciona un layout y una función por defecto que devuelve una cadena HTML:
export const config = { layout: "default" } as const;export default function homePage(c: Context): string {
const name = escapeHtml(c.req.query("name") ?? "world");
return `<h1>Hello, ${name}!</h1>`;
}
El detalle que merece atención es escapeHtml. El parámetro de consulta name está controlado por el usuario, y su interpolación directa en el marcado constituiría un caso típico de vulnerabilidad XSS reflejada. En DenoX, escapar datos no confiables no es una recomendación, sino una regla incluida en el contrato técnico del proyecto. Dado que las páginas devuelven cadenas sin formato con un motor de plantillas que no realiza escape automático, toda interpolación de datos externos debe pasar por esta función auxiliar.
Fragmentos de funcionalidades con estructura fija
En el lado de la API, cada funcionalidad es un fragmento autónomo que contiene el mismo conjunto de archivos, cada uno con una única tarea:
src/api/users/
├── user.model.ts entities only
├── user.dto.ts unknown → typed DTO (boundary validation)
├── user.repository.ts interface + default implementation
├── user.service.ts business rules only — no HTTP, no HTML
├── user.controller.ts HTTP adapter only
└── user.routes.ts composition root (constructor injection)
El módulo DTO convierte la entrada unknown en un objeto tipado en el límite de procesamiento, de modo que las capas más profundas de la pila no tienen que lidiar con cuerpos de solicitud en bruto. Los servicios contienen únicamente reglas de negocio y no saben nada sobre HTTP o HTML. Los controladores son adaptadores HTTP simples. El archivo de rutas es el punto de composición donde las dependencias se conectan mediante inyección por constructor.
Los servicios dependen de interfaces de repositorio en lugar de clases concretas. Por lo tanto, reemplazar el almacenamiento en memoria por Postgres o Deno KV implica modificar un archivo por cada funcionalidad.
Errores como excepciones tipadas
Las reglas de negocio indican un fallo lanzando excepciones tipadas. El método del servicio a continuación se niega a crear un segundo usuario con una dirección de correo electrónico ya existente:
async create(dto: CreateUserDto): Promise<User> {
const existing = await this.repository.findByEmail(dto.email);
if (existing !== null) {
throw new ConflictException(`Email "${dto.email}" is already registered`);
}
return await this.repository.create(dto);
}
El servicio no elige un código de estado ni formatea la respuesta. Un único manejador de errores centralizado asigna cada tipo de excepción a un envoltorio JSON consistente y se asegura de que las trazas de pila nunca lleguen a los clientes. Tenga en cuenta que devolver el correo electrónico permite la enumeración de cuentas, algo que podría querer evitar en endpoints públicos.
Seguridad implementada una vez, aplicada en todas partes
Las protecciones transversales se encuentran en el middleware global y no en cada funcionalidad: una política de seguridad de contenido, encabezados de respuesta reforzados, reglas CORS, verificaciones CSRF basadas en el origen, límites de velocidad según la IP del cliente, límites de tamaño del cuerpo, tiempos de espera y ocultamiento de errores internos. Las funcionalidades las utilizan en lugar de volver a implementarlas.
La configuración recibe el mismo trato. Cada variable de entorno se analiza, valida y fija al iniciar el proceso, y la aplicación se niega a arrancar si falta algo o está mal formada. En producción, CORS_ORIGIN=* es rechazado de inmediato. Es mejor fallar rápidamente al iniciar que depurar un servicio parcialmente configurado en producción.
Tres niveles de pruebas detrás de una sola orden
La configuración de pruebas va más allá de una simple afirmación genérica:
- Pruebas unitarias cubren la lógica pura mediante mocks que registran las llamadas y no requieren ningún permiso de Deno.
- Pruebas de integración prueban la aplicación completamente conectada mediante
app.request(), verificando códigos de estado, estructuras de respuesta e incluso encabezados de seguridad, sin necesidad de abrir un socket.
Deno.serve en un puerto efímero y lo consultan con llamadas reales de fetch, incluyendo una que activa intencionadamente el limitador de velocidad para confirmar que se devuelve 429.La puerta de control de calidad completa, que abarca el formato, la revisión estilística, la verificación de la tabla de rutas obsoletas, la comprobación estricta de tipos y todas las capas de pruebas, se ejecuta con deno task ci, y el pipeline de GitHub Actions realiza exactamente esa secuencia.
Un comando de despliegue, sin manejo de credenciales
El repositorio incluye manifiestos para Fly.io, Railway, Render, Docker y una unidad systemd reforzada para un VPS, además de soporte de primera clase para Deno Deploy. Una sola tarea enumera los destinos, imprime un prueba previa o ejecuta el despliegue:
deno task deploy # list targets
deno task deploy fly # dry run: steps + env reminders
deno task deploy fly --run # execute (auth delegated to the platform CLI)
La herramienta de despliegue intencionalmente nunca maneja credenciales. Verifica los requisitos previos, muestra el plan junto con recordatorios sobre las variables de entorno necesarias, y deja la autenticación en manos de la CLI oficial de cada plataforma. Los secretos permanecen completamente fuera del framework.
AGENTS.md como contrato de ingeniería vinculante
La parte más distintiva de DenoX es un archivo AGENTS.md en la raíz del repositorio, escrito como el contrato oficial tanto para los colaboradores humanos como para los agentes de programación basados en IA. Este archivo fija la pila tecnológica, define el árbol de directorios canónico y enumera las primitivas compartidas que nunca deben reinventarse: el registrador de eventos, la jerarquía de excepciones, el sobre de respuesta y el módulo de configuración.
También define un flujo de trabajo de desarrollo basado en especificaciones:
- Una especificación como
specs/feature.mdse escribe constatus: draft. - Una persona la revisa y cambia el estado a
status: approved. - Solo después de la aprobación se procede con la arquitectura, el plan, la implementación, las pruebas y la documentación.
A los agentes se les indica explícitamente que deben detenerse una vez escrita la especificación y esperar a que una persona la apruebe, de modo que un agente no puede aprobar su propio plan y luego reescribir la mitad del código. Un ciclo de referencia completo para la gestión de usuarios muestra a los agentes este patrón, y el CI aplica mecánicamente las convenciones, llegando incluso a fallar la compilación cuando un archivo generado ha sido editado a mano.
A medida que los agentes escriben más código, las convenciones solo son importantes en la medida en que se puedan verificar automáticamente; versionar el contrato junto con el código y respaldarlo con pruebas integradas convierte las pautas en límites de seguridad. Para conocer una perspectiva relacionada sobre los archivos de instrucciones para asistentes, consulte la habilidad AGENTS.md de Vercel para las mejores prácticas en React.
Ejecutarlo localmente
Clone el repositorio, cree un archivo de entorno a partir del ejemplo y inicie el servidor de desarrollo:
git clone https://github.com/olavomello/denox.git
cd denox
cp .env.example .env
deno task dev
Luego abra http://localhost:8000, llame a /api/users, envíe datos inválidos intencionadamente y verifique que el sobre de error permanezca limpio y sin rastros de pila. También está disponible una versión en tiempo real. El proyecto tiene licencia MIT; su hoja de ruta establecida incluye adaptadores para Deno KV y Postgres, registro automático de diseños, una CLI dedicada, un módulo de autenticación y generación de OpenAPI, todos ellos previstos para seguir el mismo flujo de trabajo basado en especificaciones primero. Revise el repositorio para conocer su estado actual antes de adoptarlo.
Puntos clave
- Genere tablas de rutas en el momento de la compilación, registre las rutas estáticas antes que las dinámicas, guarde el resultado y deje que CI rechace los archivos obsoletos.
- Asigne a cada funcionalidad una estructura fija: DTOs de límites, repositorios basados en interfaces, servicios sin HTTP y controladores ligeros.
AGENTS.md que exija la aprobación humana de las especificaciones, y aplica sus reglas en CI para que sean vinculantes tanto para los agentes como para las personas.Lecturas relacionadas
- Construyendo agentes de IA sobre tus servicios y API existentes de .NET — Cómo los equipos de C# pueden transformar servicios y API existentes en herramientas de agentes reguladas, con las reglas de contexto, seguridad y observabilidad que los mantienen seguros.
- De escribir Kotlin a dirigir agentes: el nuevo rol del ingeniero móvil — Cómo los agentes de programación transforman el trabajo de un ingeniero Android hacia las especificaciones, el contexto, las restricciones arquitectónicas y la verificación, y qué principios fundamentales son más importantes que nunca.