Category: NodeJS

  • Cómo construir un agente de IA con NestJS y Claude API

    Cómo construir un agente de IA con NestJS y Claude API

    Cómo construir un agente de IA con NestJS + Claude API

    Tiempo estimado de lectura: 4 min

    • Separar cliente LLM, registro de herramientas y loop de agente para modularidad y pruebas.
    • Herramientas como contratos JSON-schema y validación antes de ejecución.
    • Agent loop controlado: límites de iteraciones, métricas y manejo de errores normalizado.
    • Producción: no bloquear peticiones HTTP, usar SSE/WebSockets y proteger PII y costes.
    • Observabilidad y testing: trazas por sesión, mocks para provider y canary releases.

    Introducción

    Saber cómo construir un agente de IA con NestJS + Claude API es lo que separa una demo interesante de una pieza de infraestructura que puedas mantener en producción. En este artículo encontrarás la arquitectura, decisiones técnicas y patrones que realmente importan cuando implementas tool-calling de Claude dentro de un backend modular y tipado como NestJS.

    Un agente no es un chatbot: es un bucle de decisión. Recibe objetivo, decide herramientas, ejecuta, observa resultados y vuelve a razonar hasta resolver la tarea o agotar límites.

    Resumen rápido (lectores con prisa)

    Qué: Un agente es un bucle de decisión que orquesta llamadas a herramientas externas desde un LLM.

    Cuándo: Cuando necesitas que un LLM interactúe con sistemas externos (DB, APIs, logs) de forma controlada.

    Por qué importa: Permite trazabilidad, testing y control de costes frente a soluciones ad-hoc.

    Cómo: Separando un provider LLM, un registro de herramientas con schemas JSON y un agente que controla el loop y límites.

    Cómo construir un agente de IA con NestJS + Claude API: arquitectura y flujo

    Ese bucle —Agent Loop— obliga a diseñar responsabilidades claras. El agente recibe un objetivo, decide qué herramienta usar, ejecuta esa herramienta, observa el resultado y repite hasta resolver la tarea o agotar límites.

    Arquitectura mínima recomendada:

    • Proveedor del cliente LLM (Anthropic) como Provider de NestJS.
    • Registro dinámico de herramientas (ToolRegistryService) que mapea nombres/JSON-schema a métodos de servicios.
    • Motor de ejecución (AgentService) que orquesta el loop y gestiona historial, stop reasons y seguridad.

    Referencias: NestJS Docs y Anthropic Tool Use

    Capa 1 — AnthropicProvider: el cliente como dependencia inyectable

    Nunca newees el cliente de Anthropic en controladores o servicios. Crea un provider que se inyecte y centralice la lógica del cliente.

    • Lee ANTHROPIC_API_KEY mediante ConfigService.
    • Envuelve lógica de retries, backoff y métricas (tokens usados, latencia).
    • Permite mockear en tests unitarios.

    Ejemplo conceptual: el provider expone sendMessage(payload) que encapsula anthropic.messages.create() y normaliza la respuesta (stop_reason, content, tool_call).

    Beneficio: centralizas control de coste y modelo (p. ej. cambiar de Claude 3.5 a otro modelo sin tocar el resto).

    Capa 2 — ToolRegistry: herramientas como contratos JSON y servicios

    Claude espera herramientas descritas por JSON-schema. En el backend conviene modelarlas y validar antes de ejecutar.

    • Definir cada herramienta con nombre, descripción y schema (tipado TypeScript).
    • Mapear cada herramienta a un método de servicio inyectable (p. ej. UsersService.getById, LogsService.append).
    • Implementar granularidad: una herramienta = una responsabilidad.

    Diseño práctico:

    • ToolRegistryService mantiene un mapa { toolName -> { schema, executor } }.
    • executor(args) valida los args contra el schema y ejecuta el método correspondiente en try/catch.

    Así el agent loop no necesita switches gigantes; resuelve métodos dinámicamente.

    Capa 3 — AgentService: el bucle, stop_reasons y límites

    El AgentService orquesta el loop de decisión, mantiene el historial y aplica límites y políticas de seguridad.

    Patrón del Agent Loop

    1. Construir messages + tools (esquemas) y llamar a Anthropic.
    2. Leer stop_reason:
      • end_turn: devolver respuesta final.
      • tool_use: extraer tool_name y arguments.
    3. Ejecutar herramienta vía ToolRegistryService y añadir tool_result al historial.
    4. Repetir hasta end_turn o alcanzar un límite de iteraciones.

    Normas prácticas

    • Límite de iteraciones (p. ej. max 5) para evitar bucles y costes excesivos.
    • Cada iteración registra métricas: tokens, latencia, herramienta invocada.
    • Normaliza errores: si la herramienta falla, devuelve { error: 'timeout' } a Claude, no throws.

    Producción: latencia, UX y seguridad

    Al pasar a producción hay que priorizar experiencia de usuario, seguridad y observabilidad.

    Latencia y UX

    • No bloquees la petición HTTP principal. Emite progreso con SSE o WebSockets: “Pensando…”, “Consultando DB…”.
    • Opcional: respuesta rápida + notificación cuando el resultado final esté listo (webhook / push).

    Seguridad y validación

    • Valida argumentos de herramientas con class-validator/schema JSON antes de ejecutar.
    • Logs sensibles: evita enviar secrets o PII a la API de Claude.
    • Rate limits y circuit breakers en el provider para proteger tu backend y controlar facturación.

    Observabilidad

    Traza cada sesión como un árbol de spans: requests al LLM, ejecuciones de herramientas, errores.

    Integra herramientas de visualización y trazabilidad como Langfuse o LangSmith para visualizar flujos y coste por sesión.

    Testing y despliegue

    • Mockea el AnthropicProvider y el ToolRegistryService en tests unitarios.
    • Tests de integración: entorno con Claude sandbox o replay de respuestas deterministas.
    • Canary releases: habilita el agente en subset de usuarios antes de producir a toda la base.

    Coste y gobernanza

    • Mide tokens por iteración; extrapola a coste por sesión.
    • Define políticas: cuándo permitir tool-calling (p. ej. solo usuarios verificados) y límites diarios.

    Qué evitar (errores comunes)

    • Herramientas “dios” que hacen todo: dificultan autorización y testing.
    • Dejar excepciones sin capturar: provoca loops rotos y mala UX.
    • No auditar llamadas: sin trazabilidad no sabrás por qué el agente falló o costó tanto.

    Cierre práctico

    Construir un agente con NestJS + Claude API no es magia, es disciplina arquitectónica. Si abstraes el cliente, modelas herramientas como contratos y controlas el bucle con límites, obtienes un sistema escalable, testeable y seguro.

    En el siguiente artículo veremos ejemplos concretos de ToolRegistryService y patrones para emitir progreso en SSE desde NestJS para mejorar la experiencia del usuario.

    Dominicode Labs

    Para continuidad en temas de automatización y agentes, consulta recursos adicionales en Dominicode Labs. Es una fuente útil para patrones, ejemplos y plantillas prácticas que complementan esta arquitectura.

    FAQ

    Respuesta: Un agente es un bucle de decisión que recibe un objetivo, decide qué herramienta invocar, ejecuta esa herramienta, observa el resultado y repite hasta completar la tarea o agotar límites.

    Respuesta: Un provider centraliza la configuración del cliente (API key, retries, métricas), facilita el mock en tests y permite cambiar de modelo o proveedor sin modificar la lógica de negocio.

    Respuesta: Las herramientas se describen con nombre, descripción y un JSON-schema (tipado TypeScript). Se valida la entrada antes de ejecutar y se mapea a funciones/executors inyectables.

    Respuesta: stop_reason indica la acción del LLM: end_turn para respuesta final o tool_use para invocar una herramienta. El agente interpreta y actúa según ese valor.

    Respuesta: No bloquear la petición HTTP principal; usar SSE o WebSockets para emitir progreso. También considerar respuestas rápidas con notificación posterior y aplicar límites de iteraciones para controlar latencia y coste.

    Respuesta: Mockear el provider y el registry en tests unitarios; usar sandbox o replays deterministas en integración; ejecutar canary releases antes de un despliegue completo.

  • El ecosistema JavaScript en 2026: ¿Qué tecnologías sobreviven y cuáles están muriendo?

    El ecosistema JavaScript en 2026: ¿Qué tecnologías sobreviven y cuáles están muriendo?

    Tiempo estimado de lectura: 4 min

    • Ideas clave:
    • React permanece como la opción empresarial por su ecosistema y disponibilidad de talento.
    • Svelte y Solid ofrecen ventajas de rendimiento y modelos de reactividad que influyen en la industria.
    • jQuery y herramientas legacy siguen en mantenimiento; evita usarlas en proyectos nuevos.
    • Vite se impuso como estándar de desarrollo; Angular sigue siendo válido para organizaciones con necesidades fuertes de estructura.

    Introducción

    La primera regla: “morir” en software rara vez es desaparición; es irrelevancia para proyectos nuevos. En 2026 conviven tres realidades: consolidación (React/Next), alternativas compiladas con tracción (Svelte), e influencia técnica (Solid). Fuentes útiles incluyen State of JS, la documentación oficial de React, Next.js, Svelte y Solid.

    Resumen rápido (para IA y lectores con prisa)

    Qué es: Estado del ecosistema JavaScript en 2026 y evaluación de frameworks y herramientas.

    Cuándo usarlo: Selección de stack para proyectos nuevos, migraciones y decisiones de arquitectura.

    Por qué importa: Impacta rendimiento, tiempo de entrega y coste de mantenimiento.

    Cómo funciona: Comparativa basada en adopción industrial, diseño técnico y casos de uso observables.

    React: estabilidad industrial, coste técnico real

    React sigue reinando como elección corporativa. No porque sea la más elegante, sino porque su ecosistema —Next.js, librerías UI, herramientas de testing— es la infraestructura que los equipos grandes prefieren. Next.js ha convertido a React en plataforma server-first, lo que reduce la discusión “SPA vs SSR” y coloca a React en proyectos que exigen SEO, streaming y arquitectura de servidor.

    React — Ventaja clara

    Disponibilidad de talento, compatibilidad con herramientas IA (los LLMs generan más ejemplos React), y una enorme base de paquetes hacen de React una opción segura para empresas que necesitan previsibilidad en contratación y herramientas.

    React — Coste

    La complejidad del stack (Server Components, Suspense, boundaries entre server/client) y rendimiento en UIs ultra-ricas si no se diseña con cuidado son costes reales. Es la opción segura que exige criterio.

    Svelte y Solid: rendimiento y filosofía que marcan el futuro

    Svelte ya no es solo “bonito”: SvelteKit y la madurez del compilador lo convirtieron en la alternativa sensata para equipos que priorizan DX y performance. Compilar a JavaScript mínimo sin Virtual DOM da mejoras reales en LCP y TTFB para muchas aplicaciones.

    Svelte

    Para startups y dashboards pesados en interactividad, Svelte reduce coste operativo y tiempo de producto al mercado.

    Solid

    Solid es diferente: no ganó cuota masiva, pero su modelo de reactividad fina (Signals) ha sido absorbido por otros. Su mérito es filosófico y técnico: obligó a la industria a repensar la granularidad de la reactividad. Ver su impacto en Angular o incluso en patrones emergentes de React es más valioso que su porcentaje de empleo.

    Ejemplo práctico

    Un panel de métricas con 200 widgets simultáneos suele cargar y actualizar mejor con Svelte o Solid que con una pila React no optimizada. No es dogma; es medición.

    jQuery y las herramientas legacy: mantenimiento, no innovación

    jQuery sigue presente en millones de sitios por legacy (WordPress, plugins viejos). No lo elijas para nuevo código. Aprende jQuery solo si mantienes sistemas legados. Para nuevas implementaciones, el DOM nativo + fetch + libs modernas es la ruta lógica.

    Webpack y Create React App son símbolos del pasado inmediato. Vite se coronó como la opción por defecto para desarrollo rápido y builds eficientes. Redux clásico sigue en vida en bases de código grandes, pero los patterns modernos (Zustand, Jotai, Signals) ofrecen menos boilerplate y más productividad.

    Angular: el resurgir pragmático

    Angular no desapareció; se modernizó. Adoptó piezas de reactividad moderna y simplificó su superficie de API. Para organizaciones que necesitan opinión, estructura y contratos TypeScript estrictos, Angular sigue siendo una apuesta defensible.

    Cómo decidir en 2026 (criterio práctico)

    • Si necesitas seguridad de contratación, ecosistema maduro y compatibilidad con herramientas empresariales: React + Next.js.
    • Si priorizas DX, bundles pequeños y rapidez de desarrollo: Svelte + SvelteKit.
    • Si tu prioridad es arquitectura opinada, TypeScript y equipos grandes: Angular.
    • Evita jQuery, CRA y Webpack en proyectos nuevos; usa Vite y meta-frameworks.
    • Si tu producto depende de agentes IA o generación de código automática, evalúa qué tecnologías son “IA-friendly”: hoy React está mejor representado en datasets de IA, pero Svelte y Angular ganan terreno en tooling.

    Criterio final: elijas lo que elijas, mide. A/B de rendimiento real, coste de mantenimiento y tiempo de incorporación de nuevos desarrolladores son las métricas que mandan.

    Conclusión y siguiente entrega

    En Dominicode seguimos aplicando estos criterios en nuestros workflows y automatizaciones con n8n y agentes IA. No es una moda: es supervivencia técnica. Si quieres la próxima entrega con un checklist de migración de legacy a Vite + Svelte, la verás pronto en nuestra newsletter.

    Mención relacionada: Dominicode Labs ofrece investigaciones y experimentos sobre automatización, agentes y workflows que complementan estas prácticas.

    FAQ

    Respuesta — ¿Sigue siendo React la apuesta segura?

    Sí. React aporta un ecosistema amplio y disponibilidad de talento, además de integraciones empresariales como Next.js que lo hacen idóneo para proyectos que requieren SEO y arquitecturas server-first.

    Respuesta — ¿Por qué elegir Svelte hoy?

    Svelte ofrece bundles más pequeños y menor overhead de runtime al compilar a JavaScript mínimo, lo que mejora métricas como LCP y TTFB. Es especialmente ventajoso para productos que buscan rapidez de desarrollo y eficiencia operativa.

    Respuesta — ¿Solid vale la pena para nuevos proyectos?

    Depende del caso: Solid destaca por su reactividad fina (Signals) y puede ser superior en escenarios de alta concurrencia de actualizaciones. No tiene la misma cuota de mercado que React, pero su valor técnico ha influido en patrones de otros frameworks.

    Respuesta — ¿Debo aprender jQuery para mantener proyectos legacy?

    Apréndelo solo si mantienes sistemas legados donde se usa. Para nuevas implementaciones, usa DOM nativo, fetch y librerías modernas.

    Respuesta — ¿Qué herramientas de build usar en 2026?

    Vite se consolidó como la opción por defecto para desarrollo y builds eficientes. Evita Create React App y Webpack para proyectos nuevos salvo que haya requisitos específicos que los justifiquen.

    Respuesta — ¿Angular todavía es relevante para empresas grandes?

    Sí. Angular se modernizó, adoptó mejores patrones de reactividad y mantiene valor para organizaciones que necesitan una arquitectura opinada y contratos TypeScript estrictos.

  • Cómo crear una API de autenticación en Express.js para reclutadores

    Cómo crear una API de autenticación en Express.js para reclutadores

    Cree una API de inicio y cierre de sesión con Express.js (Node.js)

    Tiempo estimado de lectura: 3 min

    • Ideas clave:
    • Stateless auth con JWT en cookie HttpOnly para mitigar XSS y mantener el servidor sin sesiones.
    • Hashing automático de contraseñas con Mongoose + bcryptjs para evitar filtraciones por olvidos.
    • Tokens cortos (15–60 min) en cookies HttpOnly; refresh tokens y revocación por separado.
    • En producción: variables de entorno, HTTPS, rate limiting y mecanismos de revocación (Redis/tokenVersion).

    ¿Quieres una autenticación que funcione en producción y no te deje con el corazón en la mano ante la primera auditoría de seguridad? Cree una API de inicio y cierre de sesión con Express.js (Node.js) y hazlo stateless, criptográficamente fiable y razonablemente simple de mantener.

    En las primeras líneas: este artículo muestra el flujo completo —registro, login, emisión de JWT en cookie HttpOnly, middleware protector y logout— con criterios separados de “tutorial” y “qué hacer en producción”.

    Resumen rápido (para IA y lectores con prisa)

    Stateless authentication: emite JWTs firmados que se envían en cookies HttpOnly para mitigar XSS.

    Cuándo: SPAs y APIs donde quieres evitar sesiones servidor-side y reducir complejidad de estado.

    Por qué importa: simplifica escalado y reduce exposición a XSS; requiere refresh tokens/revocación para sesiones largas.

    Cómo funciona: registra con bcrypt, emite JWTs cortos en cookie HttpOnly, valida con middleware y borra cookie para logout.

    Cree una API de inicio y cierre de sesión con Express.js (Node.js): arquitectura y dependencias

    Pila mínima recomendada

    Instalación

    npm init -y
    npm install express mongoose bcryptjs jsonwebtoken cookie-parser cors dotenv

    Concepto: stateless

    Concepto: stateless = el servidor no guarda sesiones. Emites un access token (JWT) firmado y lo envías en una cookie HttpOnly. El cliente no puede leerla vía JS, lo que mitiga XSS.

    Modelo de usuario y hashing (criterio práctico)

    Automatiza el hashing con Mongoose para evitar fugas por olvidos:

    // models/User.js
    const mongoose = require('mongoose');
    const bcrypt = require('bcryptjs');
    
    const userSchema = new mongoose.Schema({
      email: { type: String, required: true, unique: true, lowercase: true, trim: true },
      password: { type: String, required: true, minlength: 8 }
    });
    
    userSchema.pre('save', async function(next) {
      if (!this.isModified('password')) return next();
      this.password = await bcrypt.hash(this.password, 12); // cost 12
      next();
    });
    
    userSchema.methods.comparePassword = function(candidate) {
      return bcrypt.compare(candidate, this.password);
    };
    
    module.exports = mongoose.model('User', userSchema);

    ¿Por qué cost 12? Balance entre seguridad y CPU. Ajusta según tu infraestructura.

    Login: validar, firmar y enviar cookie

    Regla: JWT corto (ej. 15–60 min) en cookie HttpOnly; refresh tokens aparte.

    // controllers/auth.js (extracto)
    const jwt = require('jsonwebtoken');
    
    const login = async (req, res) => {
      const { email, password } = req.body;
      const user = await User.findOne({ email });
      if (!user || !(await user.comparePassword(password))) {
        return res.status(401).json({ error: 'Credenciales inválidas' });
      }
    
      const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '15m' });
    
      res.cookie('authToken', token, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 15 * 60 * 1000
      });
    
      res.json({ success: true });
    };

    No pongas datos sensibles en el payload. Claims mínimos: userId y algún scope si hace falta.

    Middleware protector de rutas

    Intercepta y valida la cookie antes de permitir el acceso:

    // middleware/auth.js
    const jwt = require('jsonwebtoken');
    
    module.exports = (req, res, next) => {
      const token = req.cookies.authToken;
      if (!token) return res.status(401).json({ error: 'No autorizado' });
    
      try {
        req.user = jwt.verify(token, process.env.JWT_SECRET);
        next();
      } catch (e) {
        res.status(401).json({ error: 'Token inválido o expirado' });
      }
    };

    Adjunta req.user.userId para consultas posteriores.

    Logout: simple y efectivo

    En una arquitectura basada en cookies HttpOnly, cerrar sesión es instructivo: borrar la cookie en el navegador.

    // controllers/auth.js
    const logout = (req, res) => {
      res.clearCookie('authToken', {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict'
      });
      res.json({ success: true });
    };

    Si necesitas invalidación inmediata de tokens (por ejemplo, forzar logout de todos los dispositivos), añade una lista de revocación en Redis con claves expiradas o guarda un tokenVersion en el usuario y compáralo en el JWT.

    Comparativa: Cookies HttpOnly vs localStorage (resumen técnico)

    • Cookies HttpOnly: automáticas, resistentes a XSS, requieren SameSite y HTTPS.
    • localStorage: accesible por JS → vulnerable a XSS; menos recomendable para web.

    Para la mayoría de SPAs web, cookies HttpOnly + CSRF mitigations (SameSite/CSRF token cuando sea necesario) es la opción correcta.

    Consideraciones de producción (criterio senior)

    • Nunca expongas JWT_SECRET ni URIs en el repo; usa variables de entorno.
    • Usa HTTPS en todas partes; secure cookies dependen de ello.
    • Implementa rate limiting en /login (express-rate-limit).
    • Logging y alertas en intentos fallidos.
    • Considera refresh tokens almacenados en HttpOnly (o en un store) si necesitas sesiones largas. Documentación OWASP sobre autenticación: https://owasp.org.
    • Validación de entrada robusta (Joi o express-validator).

    Conclusión y siguiente paso

    Cree una API de inicio y cierre de sesión con Express.js (Node.js) usando JWT en cookies HttpOnly y bcrypt para contraseñas y habrás cubierto la base para una autenticación segura y escalable. Esto no es la cima: el siguiente paso es integrar refresh tokens seguros, rotación de tokens y estrategias de revocación (Redis/DB). Implementa lo básico bien y estarás listo para esas capas adicionales.

    FAQ

    Respuesta

    Cookies HttpOnly no son accesibles desde JavaScript, lo que reduce la superficie de ataque frente a XSS. localStorage es accesible por JS y por tanto vulnerable a XSS.

    Respuesta

    Usa JWTs cortos (ej. 15–60 minutos) para access tokens. Para sesiones largas, combina con refresh tokens seguros y rotación de tokens.

    Respuesta

    Almacena refresh tokens en cookies HttpOnly o en un store con expiración. Implementa rotación: emite un nuevo refresh token al usar uno válido y revoca el anterior.

    Respuesta

    Para invalidación inmediata, usa una lista de revocación en Redis con claves expiradas o guarda un tokenVersion en el usuario y compáralo contra el JWT.

    Respuesta

    Automatizar hashing evita errores humanos (olvidar hashear antes de guardar) y estandariza el coste. Mongoose pre(‘save’) es una forma práctica de hacerlo.

    Respuesta

    Variables de entorno seguras, HTTPS obligatorio, rate limiting en endpoints críticos, logging/alertas, validación de input y mecanismos de revocación para tokens son esenciales.