Escribe tu primer plugin backend

Objetivos del capítulo
  • Crear un plugin backend con createBackendPlugin.

  • Exponer endpoints HTTP con Express y validar la entrada con Zod.

  • Aplicar middleware de autenticación con HttpAuthService.

  • Testear el plugin con supertest.

El primer plugin: sazon-status

Hora de cocinar algo: vamos a escribir un plugin que expone el estado de cada servicio de Sazón Foods. Como una pizarra en la cocina donde el jefe anota si cada plato está listo. La pizarra se sirve vía HTTP, validada con Zod y protegida por auth.

Construimos el plugin @sazon/plugin-sazon-status-backend en el crate del capítulo.

Anatomía de un plugin backend

Un plugin backend tiene tres piezas mínimas:

Unresolved directive in chapters/cap-06-primer-plugin-backend.adoc - include::../../chapters/chapter-06-backend-plugin/src/plugin.ts[tag=plugin-skeleton]
Qué acabas de ver
  • createBackendPlugin({ id, register }) — la unidad registrada.

  • env.registerInit({ deps, init }) — declaras los coreServices que necesitas.

  • httpRouter.use(await createRouter(…​)) — montas el router Express en el HttpRouterService compartido.

El router con Zod

Cada endpoint se valida con un esquema Zod. Si la entrada no cumple, lanzamos InputError y un middleware global lo convierte en 400.

Unresolved directive in chapters/cap-06-primer-plugin-backend.adoc - include::../../chapters/chapter-06-backend-plugin/src/router.ts[tag=router-zod]
¿Por qué Zod?
  • Inmutable, declarativo, inferencia de tipos TS.

  • Genera mensajes de error legibles (input.path: message).

  • Vive con el código del plugin, no en un esquema externo.

El :apéndice F repasa TypeScript básico si vienes de otro lenguaje.

Middleware de autenticación

Cualquier endpoint serio necesita saber quién llama. Usamos HttpAuthService para extraer el Principal:

Unresolved directive in chapters/cap-06-primer-plugin-backend.adoc - include::../../chapters/chapter-06-backend-plugin/src/router.ts[tag=auth-middleware]
401 vs 403
  • 401 Unauthorized: el cliente no se autenticó (no hay token).

  • 403 Forbidden: el cliente se autenticó pero no tiene permiso (RBAC).

Si tienes RBAC configurado (ver :cap-14), usa el PermissionsService en lugar del chequeo manual.

Tests con supertest

Probar routers Express sin levantar un servidor real es trivial con supertest:

Unresolved directive in chapters/cap-06-primer-plugin-backend.adoc - include::../../chapters/chapter-06-backend-plugin/src/__tests__/router.test.ts[tag=supertest-tests]
Fakes vs mocks

En el test usamos fakes (objetos simples que cumplen la interfaz) en lugar de mocks con jest.fn(). Un fake es más legible y refleja el contrato real. Los mocks se reservan para verificar interacciones.

Registrar el plugin en el backend

Una vez escrito, lo registramos en el index.ts:

backend.add(import('@sazon/plugin-sazon-status-backend'));
Example 6. Receta del capítulo
  1. Crea el paquete plugins/sazon-status-backend con create-backend-plugin.

  2. Define createRouter() con Express + Zod.

  3. Aplica HttpAuthService a los endpoints sensibles.

  4. Escribe tests con supertest y fakes.

  5. Registra con backend.add(import('@sazon/plugin-sazon-status-backend')).

Resumen

  • Un plugin backend = createBackendPlugin() + createRouter() con HttpRouterService.

  • Zod valida entradas; InputError se traduce a 400 vía middleware.

  • HttpAuthService.credentials() + getPrincipal() da la identidad del cliente.

  • supertest permite testear routers sin servidor real.

Glosario del capítulo

Zod

Librería de validación de esquemas para TypeScript con inferencia de tipos.

Principal

Objeto que representa al usuario autenticado (subject, type).

supertest

Librería para testear servidores HTTP sin levantarlos (fake app).

fake

Implementación ligera que cumple un contrato, usada en tests.

Próximo capítulo

Persistencia, eventos y auth flow en el backend —de HTTP plano a Knex, EventsService y protección de endpoints con HttpAuthService.