services/api/src/modules/notifications/notification.client.js
Overview
Este módulo encapsula el envío de correos transaccionales de notificación mediante Resend. También centraliza la configuración obligatoria del proveedor de email y ofrece una vía controlada para sustituirlo en pruebas. El resto del sistema lo usa como cliente de salida, no como una API pública.
La configuración que exige este archivo vive en env y depende de RESEND_API_KEY, RESEND_FROM y RESEND_REPLY_TO. Si falta cualquiera de esos valores, el módulo falla con un error explícito antes de intentar enviar nada.
[!NOTE] Este módulo está pensado para infraestructura de correo transaccional. Su uso principal aparece dentro de
notification.service.js, que construye y entrega notificaciones del outbox.
How it works
El flujo del archivo es pequeño, pero muy concreto: primero resuelve el proveedor activo, luego envía el mensaje, y finalmente normaliza el resultado o el error. En pruebas, el proveedor real puede ser reemplazado temporalmente por un stub.
flowchart TD
Caller[Caller]
SendNotificationEmail[sendNotificationEmail]
GetProvider[getProvider]
EnvCheck[env resendApiKey resendFrom resendReplyTo]
ProviderOverride[providerOverride]
ResendClient[Resend client]
ProviderSend[emails send]
ConfigError[EMAIL_PROVIDER_NOT_CONFIGURED]
RejectedError[EMAIL_PROVIDER_REJECTED]
Caller --> SendNotificationEmail
SendNotificationEmail --> GetProvider
GetProvider --> EnvCheck
EnvCheck -->|missing config| ConfigError
GetProvider -->|override set| ProviderOverride
GetProvider -->|default| ResendClient
SendNotificationEmail --> ProviderSend
ProviderSend -->|provider error| RejectedError
ProviderSend -->|success| Caller
getProvider() valida la configuración obligatoria y devuelve el proveedor activo. Si existe providerOverride, lo usa; si no, crea y reutiliza una instancia de Resend. sendNotificationEmail() añade from y replyTo al mensaje, llama a provider.emails.send(), y convierte el fallo del proveedor en un error estable.
Key components
Estos son los elementos exportados o directamente responsables del comportamiento del módulo. El archivo no define clases ni enums; solo funciones y estado de módulo.
{
"title": "sendNotificationEmail",
"content": "Env\u00eda un mensaje de correo usando el proveedor activo. A\u00f1ade from y replyTo desde env y devuelve data o null.",
"type": "function",
"filePath": "services/api/src/modules/notifications/notification.client.js",
"badges": [
"public"
]
}
{
"title": "setNotificationEmailProviderForTests",
"content": "Permite inyectar un proveedor alternativo solo cuando NODE_ENV es test.",
"type": "function",
"filePath": "services/api/src/modules/notifications/notification.client.js",
"badges": [
"public"
]
}
{
"title": "resetNotificationEmailProviderForTests",
"content": "Elimina el proveedor alternativo durante tests para restaurar el comportamiento normal del m\u00f3dulo.",
"type": "function",
"filePath": "services/api/src/modules/notifications/notification.client.js",
"badges": [
"public"
]
}
Data & fields
El archivo mantiene dos variables de estado a nivel de módulo. Además, depende de tres valores de configuración externa para operar correctamente.
resendClient[badge:optional]Resend | undefined— Caché del cliente real deResend. Se crea una sola vez y se reutiliza.providerOverride[badge:optional]unknown | undefined— Proveedor alternativo usado solo en pruebas.env.resendApiKey[badge:required]string— Clave de API necesaria para construir el cliente real.env.resendFrom[badge:required]string— Dirección o identidad remitente que se inserta en cada envío.env.resendReplyTo[badge:required]string— Dirección usada comoreplyToen cada correo.
[
{
"name": "message",
"type": "object",
"required": true,
"description": "Objeto de mensaje que se pasa a provider.emails.send. El módulo le agrega from y replyTo."
},
{
"name": "options.idempotencyKey",
"type": "string",
"description": "Clave opcional enviada al proveedor para idempotencia."
}
]
Behaviors
Las funciones del archivo tienen responsabilidades separadas y muy predecibles. La validación de configuración sucede antes del envío, y los helpers de test protegen el uso fuera de entorno de pruebas.
getProvider()— Resuelve el proveedor activo. LanzaEMAIL_PROVIDER_NOT_CONFIGUREDsi falta configuración crítica.sendNotificationEmail(message, { idempotencyKey } = {})— Envía el correo confromyreplyToobligatorios, y lanzaEMAIL_PROVIDER_REJECTEDsi el proveedor responde con error.setNotificationEmailProviderForTests(provider)— Reemplaza el proveedor solo enNODE_ENV=test.resetNotificationEmailProviderForTests()— Limpia el override de pruebas cuando el entorno estest.
[!WARNING]
setNotificationEmailProviderForTests()rechaza cualquier uso fuera deNODE_ENV=test. El archivo fuerza esa restricción con un error explícito.
[!IMPORTANT] Si
resendApiKey,resendFromoresendReplyTofaltan,sendNotificationEmail()no intenta enviar nada. El error resultante usa el códigoEMAIL_PROVIDER_NOT_CONFIGURED.
[!NOTE] Si
provider.emails.send()devuelve un objeto conerror, el módulo no devuelve ese error crudo. Lo normaliza aEMAIL_PROVIDER_REJECTED.
Workflows
El flujo principal es un envío de correo con validación previa, envío al proveedor y normalización del resultado. Este camino es el que consume notification.service.js cuando entrega entradas del outbox.
sequenceDiagram
participant Caller
participant Client as notification.client
participant Provider as active provider
Caller->>Client: sendNotificationEmail message
Client->>Client: getProvider
alt missing env config
Client-->>Caller: throw EMAIL_PROVIDER_NOT_CONFIGURED
else test override or Resend client
Client->>Provider: emails send
alt provider returns error
Client-->>Caller: throw EMAIL_PROVIDER_REJECTED
else success
Client-->>Caller: data or null
end
end
sendNotificationEmail()recibe el mensaje y, opcionalmente,idempotencyKey.getProvider()valida la configuración y resuelve el proveedor activo.- El módulo llama a
provider.emails.send()confromyreplyToañadidos. - Si el proveedor falla, el archivo lanza
EMAIL_PROVIDER_REJECTED. - Si el envío tiene éxito, devuelve
result.dataonull.
Gotchas & edge cases
Este archivo tiene dos comportamientos de fallo muy importantes. Ambos son explícitos y están diseñados para que el resto del sistema pueda distinguir configuración ausente de rechazo del proveedor.
[!WARNING] La falta de cualquiera de estas variables bloquea el envío:
resendApiKey,resendFromoresendReplyTo. El error usaEMAIL_PROVIDER_NOT_CONFIGURED.
[!CAUTION] El override de proveedor solo existe para tests. Si se intenta usar en producción o en cualquier entorno distinto de
test, el módulo lanza un error inmediato.
[!NOTE] El cliente
Resendse guarda en memoria del proceso. Eso significa que el archivo reutiliza la misma instancia mientras el proceso siga vivo.
Related
Este archivo se conecta directamente con el subsistema de notificaciones y con la configuración del backend. notification.service.js lo usa para realizar el envío real del correo desde una entrada del outbox.
Los tests del backend usan setNotificationEmailProviderForTests() y resetNotificationEmailProviderForTests() para evitar llamadas reales a Resend y simular respuestas controladas. Eso aparece en pruebas como services/api/test/password-reset.test.js y services/api/test/password-create.test.js.
La configuración de entorno que alimenta este módulo vive en services/api/src/config/env.js, donde se declaran resendApiKey, resendFrom y resendReplyTo.