ecosystem-wikicómo extender los templates sin romper sus convenciones
‹ todas las recipes

Conectar con notification-api

template-api
último cambio 2026-09-25

Esta es la integración de salida con otro servicio del ecosistema (la de entrada es el webhook de tokens de push muertos, que llama notification-worker), y está montada entera con el patrón de repositorio o port. Aquí solo se cuenta el lado consumidor: cómo mandas un aviso desde tu código. Qué hace notification-api con él —plantillas, colas, entrega, logs— vive en la sección de infraestructura compartida.

Lo que hay: siete ports

Port Para qué
push-sender.port.ts Mandar un push
email-sender.port.ts Mandar un email
notification-templates-gateway CRUD de plantillas desde el panel
notification-logs-gateway Consultar el histórico de envíos
notification-provider-config-gateway Credenciales de proveedor por tenant (solo las del tenant de entrega se usan al enviar)
notification-server-tenants-gateway Listar tenants del servicio
notification-tenant-creator Dar de alta el tenant al aprovisionar

Los dos primeros son los que usarás al añadir una feature. Los otros cinco son proxies para que el panel de superadmin muestre datos que viven en el otro servicio.

Mandar un aviso

No se llama a notification-api desde un use case. Se emite un evento y el handler manda el aviso — ver evento o llamada directa:

@OnEvent(TwoFactorEnabledEvent.KEY)
async handle(event: TwoFactorEnabledEvent): Promise<void> {
  const userDevices = await this.devices.findByUserId(event.userId);
  if (userDevices.length === 0) return;
  await this.push.sendToTokens(
    userDevices.map((d) => d.token),
    { code: "account-security-updated" },
  );
}

Ese code tiene que existir como plantilla al otro lado o el envío fallará.

Dos formas de mandar los dispositivos

POST /notifications/push acepta exactamente una de estas dos formas; si llegan las dos, 400:

Campo Forma Estado
devices [{ token, platform? }], con platform en ios, android o web La forma nueva. La plataforma se guarda en el log de cada token
deviceTokens string[] Obsoleta. Se acepta, pero la API deja un warn deprecated deviceTokens en cada petición

Hoy template-api manda deviceTokens: su sendToTokens recibe solo los tokens. petid-api, el origen de esta plantilla, ya manda devices con sendToDevices(devices, payload), pasando los Device enteros. Migrar template-api está pendiente: su Device.platform es texto libre y habría que normalizarlo a esos tres valores u omitirlo.

sourceTenantId es opcional en el PushPayload y los handlers de template-api no lo mandan. Si tu producto tiene organizaciones con plantillas propias, mandarlo hace que notification-api use la plantilla de esa organización y, si no la tiene, la del tenant de entrega. Ver tenantId contra sourceTenantId. Es lo que suele llevar el tiempo de una feature de notificación, y se explica en añadir una plantilla nueva.

Los dos estilos de petición, y elegir mal

NotificationServiceHttpClient es la clase base de todos los adaptadores, y expone dos formas de llamar. La diferencia es qué pasa cuando el otro servicio no responde:

Intentos Si falla Cuándo
request() 1 Lanza BadGatewayException Alguien espera el resultado: los proxies del panel
requestWithRetry() 2 Lo registra y se lo traga Best-effort desde un handler de evento

Manda un aviso con requestWithRetry(). Consulta datos con request().

Elegir mal no da error, y ese es el problema. Usar request() en un handler hace que una excepción suba por el bus de eventos por un push que no salió; usar requestWithRetry() en un proxy del panel devuelve undefined silencioso y la pantalla sale vacía sin decir por qué.

Por qué la api arranca con notification-api caído

Cada port se resuelve por useFactory mirando una sola variable:

{
  provide: NOTIFICATION_LOGS_GATEWAY,
  inject: [ENV_CONFIG],
  useFactory: (env: EnvConfig) =>
    env.NOTIFICATIONS_BASE_URL
      ? new NotificationServiceLogsGateway(env)
      : new UnavailableLogsGateway(),
}

Sin NOTIFICATIONS_BASE_URL, cada port recibe su adaptador degradado —los cinco unavailable-* y el NoopTenantCreator— y la api arranca igual. Es lo que hace que puedas levantar el backend en local sin todo el stack detrás.

Si añades un port de notificación nuevo, añade también su versión degradada. Sin ella conviertes una dependencia opcional en obligatoria para arrancar.

Configuración

Cuatro variables, más la que exige el modo HTTP:

Variable Nota
NOTIFICATIONS_BASE_URL Su ausencia es la que activa el modo degradado
NOTIFICATIONS_API_KEY Viaja como cabecera x-api-key. Se llama igual en los dos repos, así que buscar el nombre encuentra su pareja
NOTIFICATIONS_TENANT_ID El tenant de entrega, no una organización de tu producto. Ver tenantId contra sourceTenantId
NOTIFICATIONS_TIMEOUT_MS Por defecto 5000

Ojo con la validación: en cuanto EMAIL_DRIVER=http, un superRefine exige NOTIFICATIONS_BASE_URL, _API_KEY, _TENANT_ID y APP_WEB_URL. Si falta alguna, el proceso no arranca — falla pronto y con el nombre de la variable, en vez de más tarde y en un envío suelto.

Qué no va aquí

Dar de alta plantillas, elegir canal o interpretar estados de entrega son cosas del otro lado. Si te encuentras escribiendo esa lógica en este repo, probablemente pertenece a la sección de infraestructura.