Conectar con notification-api
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 conrequest().
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.