Notificaciones — Referencia técnica
Dónde vive esto
Backend
apps/backend/graphql/resolvers/notification.resolver.js—notification,myNotifications,unreadNotificationsCount,createNotification,markNotificationAsRead,markAllNotificationsAsRead,deleteNotification,deleteAllNotifications, subscriptionnotificationAddedapps/backend/graphql/resolvers/user-notifications.resolver.js—notificationSettings,updateNotificationSettings,resetNotificationSettingsapps/backend/graphql/resolvers/user-sessions.resolver.js—registerDeviceTokenapps/backend/graphql/resolvers/call.resolver.js—registerVoIPToken,unregisterVoIPToken(usados para el push de llamadas entrantes)apps/backend/graphql/types/notification.type.js— tipo SDLNotificationy su enumapps/backend/managers/user-managers/notification.manager.js— lógica de negocio de notificaciones, agrupación y la publicación pubsub denotificationAddedapps/backend/data-access-services/user/notification.access-service.js— acceso a la base de datos de notificacionesapps/backend/services/notification-queue.service.js— cola BullMQ que respalda anotification.worker.jsapps/backend/services/message-notification.service.js— enruta eventos de nuevos mensajes hacia el pipeline de notificaciones/pushapps/backend/services/notification/notification.service.js— envoltorio delgado que delega en la fachadaservices/push-notification/apps/backend/services/push-notification/push-notification.service.js— fachada de push independiente del proveedor (la variable de entornoPUSH_NOTIFICATION_PROVIDERselecciona el proveedor, por defectofirebase);providers/firebase.provider.jses el proveedor actual (y el único)apps/backend/services/firebase.service.js— inicialización del Firebase Admin SDK, compartido por la entrega de push y la verificación de tokens de Apple-authapps/backend/services/apns.service.js— punto de entrada retrocompatible que ahora simplemente delega en la fachadapush-notification(el push normal de iOS/Android/web pasa todo por FCM; ya no se comunica directamente con APNs)apps/backend/services/voip-push.service.js— integración APNs real y separada (@parse/node-apn) usada solo para el push VoIP de llamadas entrantes (usada porcall.manager.js/call.resolver.js)
Frontend
apps/frontend-nextjs/src/app/notifications/page.tsx— ruta de la página de notificacionesapps/frontend-nextjs/src/components/notifications/NotificationsPanel.tsx— lista/desplegable de notificacionesapps/frontend-nextjs/src/components/notifications/NotificationsButton.tsx— ícono de campana con indicador de no leídasapps/frontend-nextjs/src/page-components/settings/NotificationsSettingsPage.tsx— UI de configuración por canal
Checklist de implementación técnica
-
myNotifications/markNotificationAsRead/markAllNotificationsAsRead— resolvers conectados ennotification.resolver.js;NotificationsPanel.tsxconsulta, muestra y marca los elementos como leídos -
firebase.service.js/ fachadapush-notification//notification-queue.service.js— implementan por completo la entrega de push del lado del servidor;WebPushRegistrar.tsx(montado una vez para los usuarios autenticados, junto aUsageHeartbeat) ahora llama aregisterDeviceTokenconplatform: 'web'cada vez queinitWebPush()resuelve un token, así que el push ahora sí se entrega al cliente web una vez que existe la configuración web de Firebase (antes el frontend nunca llamaba a esta mutation) -
notificationSettings/updateNotificationSettings/resetNotificationSettings— resolvers conectados enuser-notifications.resolver.js;NotificationsSettingsPage.tsxconsulta y modificanotificationSettings(canales de email/push/SMS) directamente, y por separado consulta/modifica un subconjuntopauseAll/quietHoursEnabled/messagesOnlyModedeextendedNotificationSettingspara sus interruptores del panel principal — llamadas GraphQL reales, no interruptores estáticos -
unreadNotificationsCount/ subscriptionnotificationAdded—NotificationsButton.tsxyNotificationsPanel.tsxse suscriben anotificationAddedy recargan el contador/la lista con cada evento, lo que reemplaza el polling anterior de 30 segundos por actualizaciones impulsadas por push
Tipos de notificación
El enum ha crecido mucho más allá del conjunto social original para cubrir eventos de comercio, moderación y seguridad de cuenta:
enum NotificationType {
like # Reaction on your post
comment # Comment on your post
follow # New follower
follow_request # Someone requested to follow you (private account)
mention # Mentioned in a comment or message
tag # Tagged in a post
message # New direct message
message_purchase # Someone bought a paid message
post # Post from someone you follow
post_purchase # Someone bought a paid post
repost # Your post was shared
story_like # Reaction on your story
reaction # Reaction on your story (alt path)
subscription # New subscription to your profile
subscription_renewal_reminder # Upcoming subscription renewal
conversation_subscription # New paid-chat subscription
tip # You received a tip
cashout # Cashout status update
product_purchase # Someone bought your product
order_shipped # Order shipped
order_canceled # Order canceled
dispute_opened # Payment dispute opened
dispute_resolved # Payment dispute resolved
identity_verification_approved # ID verification approved
identity_verification_rejected # ID verification rejected
referral_bonus # Referral bonus earned
collaboration_invite # Invited to collaborate on a post
collaboration_accepted # Collaboration invite accepted
collaboration_rejected # Collaboration invite declined
collaboration_removed # Removed from a collaboration
security_issue # Security alert
system # System notification
system_alert # System notification (alt path)
new_report # New report filed (moderation)
moderation_action # Account moderation action taken
}
DEFAULT_PUSH_TITLES en notification.manager.js mapea cada tipo a su título de notificación push. Los tipos sociales en GROUPABLE_PUSH_TYPES (like, comment, follow, follow_request, repost, story_like, reaction, tag) tienen sus ráfagas de push colapsadas en una sola notificación por ventana de 5 minutos; los tipos transaccionales/de mensajes/de seguridad nunca se agrupan.
Modelo Notification
| Campo | Descripción |
|---|---|
notificationType | Tipo (ver enum) |
actorId / actor | Quién generó la notificación |
entityType / entityId | Contenido relacionado |
message | Texto de la notificación |
isRead / readAt | Estado de lectura |
groupKey / groupCount | Agrupación (p. ej. "5 me gusta") |
metadata | Datos adicionales (JSON) |
Queries
myNotifications devuelve la lista de notificaciones del usuario actual, de más reciente a más antigua. Las notificaciones con el mismo groupKey se agrupan — groupCount indica cuántos eventos se colapsaron en un solo elemento (p. ej. "Alice, Bob y otras 3 personas reaccionaron a tu publicación"). Cada elemento también resuelve un campo post (miniatura para notificaciones relacionadas con publicaciones) y un campo actor, ambos resueltos bajo demanda si no se cargaron de forma anticipada.
notification(id) obtiene una sola notificación por ID (autenticado, con verificación de propiedad en el manager).
unreadNotificationsCount es un contador económico — llámalo al montar la app; después el frontend lo mantiene actualizado mediante la subscription notificationAdded en lugar de polling.
notificationSettings devuelve el estado de los interruptores por canal del usuario — útil para renderizar la UI de configuración.
query MyNotifications($limit: Int, $offset: Int) {
myNotifications(limit: $limit, offset: $offset) {
id notificationType message isRead createdAt
actor { username profilePicture }
entityType entityId
groupKey groupCount
post { id media { thumbnailUrl mediaUrl } }
}
}
query UnreadCount { unreadNotificationsCount }
query NotificationSettings {
notificationSettings {
emailNotifications pushNotifications smsNotifications
likesNotifications commentsNotifications followsNotifications
mentionsNotifications messagesNotifications postsNotifications
marketingNotifications securityNotifications
}
}
Mutations
markNotificationAsRead marca un solo elemento como leído y actualiza readAt. markAllNotificationsAsRead es la versión masiva — llámala cuando el usuario abre el panel de notificaciones.
deleteNotification elimina permanentemente un elemento. deleteAllNotifications borra todo el historial.
createNotification crea una notificación para un userId arbitrario — pensada para uso de sistema/administración, no está expuesta en la UI normal.
updateNotificationSettings guarda los interruptores por canal. resetNotificationSettings revierte todos los interruptores a los valores predeterminados de la plataforma.
mutation MarkRead($notificationId: ID!) { markNotificationAsRead(notificationId: $notificationId) }
mutation MarkAllRead { markAllNotificationsAsRead }
mutation DeleteNotification($id: ID!) { deleteNotification(notificationId: $id) }
mutation DeleteAllNotifications { deleteAllNotifications }
mutation UpdateNotificationSettings($input: NotificationSettingsInput!) {
updateNotificationSettings(input: $input) { success }
}
mutation ResetNotificationSettings { resetNotificationSettings }
Subscription
notificationAdded(userId: ID!) se dispara para el destinatario cada vez que se crea una nueva notificación para él. NotificationsButton.tsx y NotificationsPanel.tsx se suscriben a ella (limitada al propio ID del usuario que inició sesión) y recargan el contador de no leídas / la lista con cada evento — esto reemplazó el intervalo de polling anterior de 30 segundos.
subscription OnNotificationAdded($userId: ID!) {
notificationAdded(userId: $userId) { id }
}
Configuración por canal
Cada canal se activa o desactiva de forma independiente:
| Configuración | Descripción |
|---|---|
emailNotifications | Entrega por correo electrónico |
pushNotifications | Entrega push por FCM |
smsNotifications | Entrega por SMS |
likesNotifications | Reacciones a publicaciones |
commentsNotifications | Comentarios en publicaciones |
followsNotifications | Nuevos seguidores |
mentionsNotifications | Menciones en contenido |
messagesNotifications | Mensajes directos |
postsNotifications | Publicaciones de usuarios seguidos |
marketingNotifications | Noticias y novedades de la plataforma |
securityNotifications | Alertas de seguridad |
Notificaciones push (FCM)
El backend envía notificaciones push mediante Firebase Cloud Messaging, a través de una fachada independiente del proveedor (services/push-notification/) en la que delegan tanto apns.service.js como services/notification/notification.service.js:
- Al iniciar sesión (web) o al abrir la app (iOS), el frontend registra el token del dispositivo (
registerDeviceToken) — en web esto lo haceWebPushRegistrar.tsx, condicionado a la configuración web de Firebase notification.worker.jsse ejecuta como un job en segundo plano de BullMQ (concurrencia 10, hasta 100/seg) para despachar los push- El backend usa el Firebase Admin SDK, inicializado en
firebase.service.jsa partir de las variables de entornoFIREBASE_PROJECT_ID/FIREBASE_PRIVATE_KEY/FIREBASE_CLIENT_EMAIL PUSH_NOTIFICATION_PROVIDER(por defectofirebase) selecciona el proveedor de entrega en la fachada;DISABLE_PUSH_NOTIFICATIONScorta en seco todos los envíos y los reemplaza por respuestas simuladas (usado en dev/test)
Para iOS, el push VoIP de llamadas entrantes se maneja por separado mediante APNs real (@parse/node-apn en voip-push.service.js, configurado con APNS_PRIVATE_KEY / APNS_KEY_ID / APNS_TEAM_ID / APNS_BUNDLE_ID) en lugar de pasar por la fachada de FCM — ver registerVoIPToken.
Configuración extendida
extendedNotificationSettings / updateExtendedNotificationSettings (graphql/types/extended-notification-settings.type.js + resolver) es una superficie separada y mucho más amplia, construida encima de los canales base descritos arriba — el detalle completo vive en Configuración → Notificaciones, ya que se construyó como parte del trabajo de la funcionalidad de Configuración y no de esta. Respaldada por notification-settings.manager.js, que ya tenía muchas más claves internas de las que se llegaron a exponer a través del tipo base NotificationSettings — la mayoría de los campos extendidos son simplemente estado preexistente del manager recién expuesto, no lógica de negocio nueva.