Skip to Content
Notification Documentation

Notification Documentation

This documentation explains the features and usage of Notification Module: Located at src/modules/notification

Overview

The notification module provides a comprehensive multi-channel notification system backed by three dedicated BullMQ queues: one for orchestration, one for email delivery, and one for push delivery.

Key features:

  • Multi-Channel Delivery: email, push, inApp, and silent channels
  • Queue-Based Processing: Three separate BullMQ queues for orchestration, email, and push
  • User Preference Control: Per type+channel opt-in/out settings for each user
  • AWS SES Email Templates: Handlebars .hbs templates synced to SES via NotificationTemplateService
  • Firebase FCM Push: Multicast delivery with batch chunking, rate limiting, and stale token cleanup
  • Delivery Tracking: Per-channel processedAt, sentAt, and failureTokens recorded on each delivery record; silent and inApp are pre-marked at creation time

Table of Contents

Notification Types and Priorities

Defined in EnumNotificationType:

TypeDescription
userActivityGeneral activity events (welcome, email verified, etc.)
securityAlertSecurity-sensitive events (new device login, password change, 2FA reset)
marketingPromotional and marketing messages
transactionalSystem-driven transactional events (e.g., term policy published)

Defined in EnumNotificationPriority:

PriorityDescription
normalStandard informational notifications
highImportant events requiring prompt attention
criticalSecurity or time-sensitive events

Notification Channels

Each notification record carries one or more NotificationDelivery rows, one per channel. Available channels:

ChannelDeliveryprocessedAt / sentAt
emailVia AWS SES (queued)Set by NotificationEmailProcessorService after send
pushVia Firebase FCM (queued)Set by NotificationPushProcessorService after send
inAppIn-application UIPre-filled at notification creation time
silentNo external delivery; record-onlyPre-filled at notification creation time

A notification can target multiple channels simultaneously. The channels used for each notification type are defined per-event in NotificationRepository when the record is created.

inApp and silent are considered immediately “delivered” — their processedAt and sentAt are both set at creation time in the repository, so no separate queue job is needed for them. Only email and push deliveries go through the async queue.

Queue Architecture

The notification module uses three dedicated BullMQ queues, each with its own processor:

NotificationUtil → EnumQueue.notification → NotificationProcessor NotificationEmailUtil → EnumQueue.notificationEmail → NotificationEmailProcessor NotificationPushUtil → EnumQueue.notificationPush → NotificationPushProcessor

Orchestration Queue

Queue: EnumQueue.notification | Processor: NotificationProcessor | Service: NotificationProcessorService

Handles the main event orchestration. When a process job is consumed, NotificationProcessorService:

  1. Fetches the target user
  2. Creates the Notification record (with delivery rows) in the database
  3. Dispatches jobs to notificationEmail and/or notificationPush queues as appropriate

Jobs are dispatched via NotificationUtil, which applies deduplication per userId with a 1-second TTL.

Supported processes (EnumNotificationProcess):

Job NameDescription
welcomeByAdminAdmin-created user welcome
welcomeSelf-registered user welcome + verification email
welcomeSocialSocial login welcome
temporaryPasswordByAdminTemporary password assigned by admin
changePasswordUser changed their password
verifiedEmailEmail address verified
verifiedMobileNumberMobile number verified
verificationEmailStandalone email verification
forgotPasswordForgot password request
resetPasswordPassword was reset
newDeviceLoginLogin detected from a new/unknown device
resetTwoFactorByAdminAdmin reset user 2FA
publishTermPolicyNew term policy published (bulk, all active users)

Email Queue

Queue: EnumQueue.notificationEmail | Processor: NotificationEmailProcessor | Service: NotificationEmailProcessorService

Rate-limited to match the AWS SES sending quota (AwsSESRateLimitPerDuration per AwsSESRateLimitDurationInMs).

Each job calls AwsSESService.send() or AwsSESService.sendBulk() using the named SES template for that event, with defaultTemplateData (homeName, supportEmail, homeUrl) merged automatically.

Jobs are dispatched via NotificationEmailUtil, which uses jobId-based deduplication per userId.

Push Queue

Queue: EnumQueue.notificationPush | Processor: NotificationPushProcessor | Service: NotificationPushProcessorService

Rate-limited to FirebaseMaxRateLimitPerDuration (500,000) per FirebaseRateLimitDurationInMs (60 seconds), keeping safely under the FCM 600k/min ceiling.

Supported push processes (EnumNotificationPushProcess):

Job NameDescription
newDeviceLoginPush alert for new device login
resetPasswordPush alert when password is reset
resetTwoFactorByAdminPush alert when admin resets 2FA
temporaryPasswordByAdminPush alert for temporary password
cleanupTokensRemove reported invalid FCM tokens
cleanupStaleTokensClean up tokens inactive for ≥ 60 days

On onModuleInit, NotificationPushProcessorService automatically dispatches a cleanupStaleTokens job.

Push Notifications

Push Token Management

Push tokens (FCM device tokens) are part of the Device module (src/modules/device), not stored in the notification module directly. NotificationPushProcessorService retrieves active tokens from DeviceOwnershipRepository before dispatching FCM calls.

For push token registration, revocation, and session-linking details, see the Device documentation.

Token Cleanup Strategy

After each multicast send, FirebaseService.sendMulticast() returns failureTokens — tokens that FCM identified as invalid (codes in FirebaseInvalidTokenCodes). These are:

  1. Stored on the delivery record via NotificationRepository.updateSentAt() (failureTokens field)
  2. Queued as a cleanupTokens job in EnumQueue.notificationPush, handled by NotificationPushProcessorService.processCleanupTokens()

Stale tokens — those with no activity for FirebaseStaleTokenThresholdInDays (30 days) — are pruned at startup via cleanupStaleTokens.

FCM Rate Limiting

The push processor is configured with a BullMQ rate limiter:

@QueueProcessor(EnumQueue.notificationPush, { limiter: { max: FirebaseMaxRateLimitPerDuration, // 500,000 duration: FirebaseRateLimitDurationInMs, // 60,000 ms }, })

FirebaseService.sendMulticast() also enforces a per-call chunk size of at most FirebaseMaxSendPushBatchSize (500) tokens per FCM sendEachForMulticast call, with chunks processed via Promise.allSettled.

For Firebase configuration and no-op mode (disabled when credentials are missing), see Third-Party Integration — Firebase.

Email Notifications

Template System

Email templates are Handlebars (.hbs) files located in src/modules/notification/templates/. They are uploaded to AWS SES as named templates via NotificationTemplateService, which provides import, get, and delete operations per template.

Available templates (one per EnumNotificationProcess that uses email):

Template FileProcess
notification.welcome.template.hbswelcome
notification.welcome-social.template.hbswelcomeSocial
notification.welcome-by-admin.template.hbswelcomeByAdmin
notification.temporary-password-by-admin.template.hbstemporaryPasswordByAdmin
notification.change-password.template.hbschangePassword
notification.forgot-password.template.hbsforgotPassword
notification.new-device-login.template.hbsnewDeviceLogin
notification.reset-password.template.hbsresetPassword
notification.verification-email.template.hbsverificationEmail
notification.verified-email.template.hbsverifiedEmail
notification.verified-mobile-number.template.hbsverifiedMobileNumber
notification.reset-two-factor-by-admin.template.hbsresetTwoFactorByAdmin
notification.publish-term-policy.template.hbspublishTermPolicy

Templates are managed independently from migrations. Use NotificationTemplateService methods (e.g., emailImportWelcome(), emailDeleteWelcome()) to sync them to SES.

For AWS SES configuration and no-op mode, see Third-Party Integration — SES.

Delivery Tracking

Each Notification record contains embedded NotificationDelivery rows (one per channel). Delivery fields:

FieldDescription
processedAtWhen the processor started handling the delivery
sentAtWhen the message was sent to the provider (FCM / SES)
failureTokensFCM tokens that were invalid (push channel only)

Immediate channels (silent, inApp)

silent and inApp deliveries have processedAt and sentAt both pre-filled with the current timestamp at notification creation time inside NotificationRepository. No queue job is dispatched for them.

Async channels (email, push)

email and push deliveries are initially created with no timestamps and go through the full queue lifecycle:

User Notification Settings

Users control which notification channels are active per type. Settings are stored in NotificationUserSetting (one row per userId + type + channel).

Allowed type+channel combinations are defined in NotificationSettingUpdateAllowedCombinations:

TypeAllowed Channels
userActivityemail, inApp, push
marketingemail, push

NotificationService.updateUserSetting() validates the requested combination before writing. Invalid combinations throw a BadRequestException with EnumNotificationStatusCodeError.invalidType or invalidChannel.

The request DTO (NotificationUserSettingRequestDto) accepts:

  • type: userActivity | marketing
  • channel: email | push | inApp
  • isActive: boolean

Contribution

Special thanks to ak2g  for contributing to the Notification module implementation.

Last updated on