New Backend System: la cocina real

Objetivos del capítulo
  • Montar el backend con createBackend() y añadir features.

  • Entender backend features y módulos como unidades reusables.

  • Mapear los coreServices a sus implementaciones por defecto.

  • Manejar la configuración por capas (app-config.yaml + locales).

Más allá del index.ts legacy

En el :cap-02 vimos la cocina desde fuera. Ahora abrimos la puerta de servicio y nos ponemos el delantal: el index.ts ya no se llena de routers, sino de líneas backend.add(import('…​')). Cada línea es una receta que la cocina monta al arrancar.

La New Backend System es la arquitectura recomendada desde Backstage 1.20. Sustituye al legacy index.ts con routers manuales por una API limpia basada en createBackend() y BackendFeature.

createBackend y backend features

El archivo de entrada del backend es minimalista:

backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@sazon/plugin-sazon-status-backend'));
Qué hace backend.add(…​)

Cada add registra una BackendFeature (un plugin, un módulo o un servicio). El backend las monta en orden y resuelve sus dependencias por tipo. No hay routers manuales: el HttpRouterService se inyecta a quien lo pida.

Diagram
Figure 8. Creación y arranque del backend
Diferencia con la legacy

Legacy: montas routers en index.ts con app.use(…​) y arrancas Express. New Backend System: declaras features, el framework las monta y resuelve dependencias.

Servicios core

El backend expone un bus de servicios llamado coreServices. Cada feature pide los que necesita y el framework los inyecta. Los servicios clave son:

  • DatabaseService — Knex configurado para Postgres en prod, SQLite en dev.

  • CacheService — backend de cache (in-memory en dev, redis en prod).

  • HttpAuthService — convierte el header de identidad en un Principal.

  • LoggerService — pino con JSON estructurado.

  • ConfigService — expone la configuración fusionada por capas.

  • HttpRouterService — router Express compartido donde los plugins montan sus endpoints.

  • SchedulerService — tareas programadas.

  • DiscoveryService — endpoints internos (catalog, auth, scaffolder).

El framework los resuelve por tipo: tu plugin declara deps: { logger: coreServices.logger } y el backend pasa la implementación correcta en init().

// coreServices are resolved lazily by the framework:
//   - DatabaseService  -> better-sqlite3 in dev, postgres in prod
//   - CacheService     -> in-memory in dev, memcached/redis in prod
//   - HttpAuthService  -> wraps the identity header into a Principal
//   - LoggerService    -> pino with structured JSON
//   - ConfigService    -> composes app-config*.yaml layers in order

Configuración por capas

Backstage carga la configuración desde varios archivos YAML en este orden:

  1. app-config.yaml — base, en git.

  2. app-config.local.yaml — overrides locales del developer, gitignored.

  3. app-config.production.yaml — overrides para producción, según NODE_ENV.

Las claves se fusionan con deep merge: la última capa gana en claves hoja, los mapas y arrays se concatenan selectivamente.

backend:
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: ${POSTGRES_PORT}
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
  cache:
    store: redis
    connection: ${REDIS_URL}
  baseUrl: http://localhost:7007
  cors:
    origin: http://localhost:3000
Buenas prácticas con config
  • Lee valores con :code:`config.getString('backend.baseUrl')` desde tus servicios, nunca los hardcodes.

  • Para secretos usa :code:`config.getString('secrets.foo')` y remítelos a variables de entorno.

  • Documenta cada clave en app-config.yaml con comentarios al lado de la clave.

Backend features: plugins vs módulos

Una BackendFeature puede ser de tres tipos:

Diagram
Figure 9. Backend features y módulos
  • PlugincreateBackendPlugin({ id, register(env) }). Define un endpoint nuevo (ej. scaffolder).

  • ModulecreateBackendModule({ pluginId, register(env) }). Añade comportamiento a un plugin existente (ej. un processor del catalog).

  • Service factorycreateServiceFactory({ service, factory }). Define un servicio core customizado.

Example 5. Receta del capítulo
  1. Sustituye el index.ts legacy por uno que use createBackend().

  2. Añade cada plugin como backend.add(import('…​')).

  3. Lee la configuración desde el service, no desde variables de entorno sueltas.

  4. Si necesitas un plugin existente con cambios, escribe un módulo en lugar de forkear.

Migración desde legacy

La legacy backend system sigue operativa en Backstage 1.x, pero el blog oficial la recomienda como puente temporal. La migración se hace así:

  1. Reescribe index.ts para usar createBackend().

  2. Convierte cada createPlugin({ router }) a createBackendPlugin({ id, register }).

  3. Si tenías routers custom, móntalos con httpRouter.use(await createRouter(…​)) dentro de un plugin.

  4. Borra el archivo legacy una vez arrancada la nueva cocina.

El :apéndice H entra en detalle.

Resumen

  • La New Backend System es la arquitectura por defecto desde 1.20.

  • createBackend() + backend.add(feature) reemplaza el legacy index.ts.

  • Los coreServices resuelven dependencias por tipo.

  • La configuración se compone por capas (base + local + producción).

Glosario del capítulo

BackendFeature

Unidad que la cocina monta: plugin, módulo o service factory.

coreServices

Bus de servicios resueltos por tipo (Database, Cache, Auth, Logger, etc.).

config layers

Archivos app-config*.yaml que se fusionan en orden.

HttpRouterService

Servicio que expone el router HTTP compartido.

HttpAuthService

Servicio que convierte credenciales HTTP en Principal.

Próximo capítulo

Escribe tu primer plugin backend —de la teoría al primer createBackendPlugin() con router Zod y tests con supertest.