Persistencia, eventos y auth flow en el backend

Objetivos del capítulo
  • Persistir datos con Knex y migraciones versionadas.

  • Configurar el plugin-database para datos de aplicación.

  • Publicar y consumir eventos del EventsService.

  • Proteger endpoints con HttpAuthService y verificar identidad.

Tres bases de datos, tres capas

Backstage cocina con tres ollas separadas: la del catálogo (Postgres + tabla de entidades), la de plugins (la misma DB, esquema separado por plugin), y la de tu aplicación (tu DB si quieres aislar). Cada olla tiene su propósito.

Tres capas de DB en Backstage
Failed to generate image: Could not find the 'mmdc' executable in PATH; add it to the PATH or specify its location using the 'mmdc' document attribute
flowchart LR
  Catalog[(Catalog DB)] --> CatalogPlugin[Catalog Plugin]
  PluginDB[(Plugin DB)] --> PluginCode[Sazon Plugins]
  AppDB[(App DB)] --> AppCode[Sazon Business]
  CatalogPlugin --> CatalogDB[(Catalog DB)]
  PluginCode --> PluginDB[(Plugin DB)]
  AppCode --> AppDB[(App DB)]

Migraciones con Knex

Las migraciones son archivos versionados que aplican cambios a la DB. Viven en plugins/<plugin>/migrations/<timestamp>_<name>.ts:

Unresolved directive in chapters/cap-07-persistencia-eventos-auth-flow.adoc - include::../../chapters/chapter-07-backend-advanced/migrations/20260101_status_changes.ts[tag=knex-migrations]
Convención de nombres
  • Timestamps en formato YYYYMMDDHHMMSS.

  • up() aplica; down() revierte.

  • Las migraciones son inmutables: una vez en producción, no las editas; creas una nueva.

plugin-database: el servicio de DB

Para que tu plugin use una DB, no escribes new Knex(…​). Pides DatabaseService al framework:

Unresolved directive in chapters/cap-07-persistencia-eventos-auth-flow.adoc - include::../../chapters/chapter-07-backend-advanced/src/database.ts[tag=plugin-database-setup]
¿Por qué no instanciar Knex?

Porque el framework ya configuró el cliente con la conexión, el pool y las migraciones. Si lo instanciaras a mano, te saltas las migraciones del framework y rompes el ciclo de vida.

Dónde corre

plugin-database crea un esquema por plugin dentro de la misma DB Postgres. Los datos quedan aislados por esquema, no por DB física. Para aislar físicamente, configura una conexión distinta en app-config.yaml.

Eventos: el bus interno

El EventsService es un bus pub/sub interno. Útil para reaccionar a cambios sin acoplar plugins.

Unresolved directive in chapters/cap-07-persistencia-eventos-auth-flow.adoc - include::../../chapters/chapter-07-backend-advanced/src/events.ts[tag=events-publish-consume]
Eventos vs webhooks
  • Eventos viven dentro del proceso de Backstage. Son baratos y síncronos.

  • Webhooks salen al exterior. Son útiles para integraciones con terceros (GitHub, GitLab).

No confundas: el EventsService no es para notificar a sistemas externos.

Pub/sub del EventsService
Failed to generate image: Could not find the 'mmdc' executable in PATH; add it to the PATH or specify its location using the 'mmdc' document attribute
sequenceDiagram
  participant Pub as Plugin A
  participant Bus as EventsService
  participant Sub1 as Plugin B
  participant Sub2 as Plugin C
  Pub->>Bus: publish(topic, payload)
  Bus->>Sub1: onEvent(topic, payload)
  Bus->>Sub2: onEvent(topic, payload)

Auth flow: proteger endpoints

Proteger un endpoint es extraer el Principal del request:

Unresolved directive in chapters/cap-07-persistencia-eventos-auth-flow.adoc - include::../../chapters/chapter-07-backend-advanced/src/http-auth.ts[tag=http-auth-protect]
No confundir autenticación con autorización
  • Autenticación = quién es (HttpAuthService.credentials).

  • Autorización = qué puede hacer (PermissionsService en :cap-14).

El http-auth-protect resuelve solo la primera. Para la segunda, usa el PermissionPolicy del RBAC plugin.

Example 7. Receta del capítulo
  1. Crea una migración versionada con up() y down().

  2. Pide DatabaseService en registerInit({ deps }) para aplicar migraciones.

  3. Publica eventos con EventsService.publish({ topic, eventPayload }).

  4. Protege endpoints con HttpAuthService.credentials() + getPrincipal().

  5. Si necesitas permisos, delega a PermissionsService.

Resumen

  • Las migraciones viven en migrations/ y son versionadas por timestamp.

  • DatabaseService da un cliente Knex configurado por el framework.

  • EventsService es el bus pub/sub interno, no un sistema de webhooks.

  • Proteger endpoints = HttpAuthService.credentials() + getPrincipal().

Glosario del capítulo

migración

Archivo versionado que aplica un cambio de esquema a la DB.

Knex

SQL query builder para Node, usado por Backstage como capa de DB.

EventsService

Bus pub/sub interno, síncrono y en proceso.

PermissionsService

Servicio que aplica políticas de RBAC del plugin @backstage/plugin-permission-backend.

esquema

Agrupación lógica de tablas dentro de una DB Postgres.

Próximo capítulo

Tu primer plugin frontend —cambiamos de piso: del backend al frontend, React + Material UI + createPlugin.

Parte III: Parte 3 — Frontend: la sala y la carta

React, Material UI, plugins frontend, componentes y el menú del portal. Lo que el developer ve, toca y firma.