Skip to main content

Notifications Module

The Notifications module is a comprehensive push notification and in-app messaging system for the Comdeall platform that manages the complete lifecycle of notifications including Firebase Cloud Messaging (FCM) integration, device token management, template-based notifications, bulk messaging, failed token cleanup, and background job processing. It provides real-time communication capabilities across mobile and web platforms with sophisticated retry mechanisms and delivery tracking.

Table of Contents

  1. Module Structure
  2. Notification Endpoints
  3. Core Features
  4. Notification Types & Workflows
  5. Firebase Integration
  6. Background Job Processing
  7. Device Token Management
  8. Template System
  9. Security & Authorization
  10. Technical Implementation

Module Structure

@Module({
imports: [DBModule, forwardRef(() => BackgroundModule)],
controllers: [NotificationsController],
providers: [
NotificationsService,
{
provide: 'FirebaseProvider',
useClass: FirebaseProvider,
},
],
exports: [NotificationsService],
})
export class NotificationsModule {}

Architecture Components

  • NotificationsController - REST API endpoints (/api/communication)
  • NotificationsService - Business logic and workflow orchestration
  • FirebaseProvider - Firebase Cloud Messaging integration
  • NotificationsDBService - Database operations and token management
  • Background Jobs - Async notification processing and retry logic
  • Template Engine - Pre-defined notification templates

Notification Endpoints

MethodEndpointAuthRolesDescription
POST/send-notificationJWTADMINSend template-based notifications
POST/send-single-notificationJWTADMINSend notification to single device
POST/send-custom-notificationJWTADMINSend custom notifications to multiple users

Core Features

1. Multi-channel Notifications

  • Push Notifications - Firebase Cloud Messaging for mobile and web
  • In-app Notifications - Database-stored notifications for app inbox
  • Template System - Pre-defined notification templates
  • Custom Notifications - Ad-hoc notifications with dynamic content

2. Device Management

  • Token Registration - Automatic device token registration and updates
  • Token Cleanup - Automatic removal of expired and invalid tokens
  • Multi-device Support - Users can have multiple registered devices
  • Platform Support - iOS, Android, and web push notifications

3. Delivery Assurance

  • Retry Mechanisms - Automatic retry for failed deliveries
  • Error Handling - Comprehensive error classification and handling
  • Delivery Tracking - Track successful and failed notification deliveries
  • Dead Letter Queue - Failed jobs moved to DLQ for analysis

4. Background Processing

  • Async Delivery - Non-blocking notification processing
  • Batch Processing - Efficient bulk notification handling
  • Queue Management - Redis-backed job queue with concurrency control
  • Rate Limiting - Prevents API rate limit violations

Notification Types & Workflows

Template-based Notifications

Admin selects template → Users selected → Device tokens fetched → Firebase delivery → Database logging → Cleanup failed tokens

Use Cases:

  • System announcements
  • Feature updates
  • Promotional messages
  • Scheduled reminders

Custom Notifications

Custom content created → Target users identified → In-app notification saved → Push notification sent → Response tracking

Use Cases:

  • Assessment assignments
  • Appointment reminders
  • Progress updates
  • Personalized messages

Single Device Notifications

Device token provided → Direct Firebase call → Immediate response → Error handling

Use Cases:

  • Testing and debugging
  • Direct device targeting
  • Emergency notifications
  • Development testing

Automated System Notifications

System event triggered → Background job queued → Notification template applied → Multi-channel delivery → Audit logging

Use Cases:

  • User inactivity reminders
  • Appointment confirmations
  • Payment confirmations
  • System maintenance alerts

Firebase Integration

Firebase Cloud Messaging Setup

// Firebase configuration
{
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n')
}

Single Notification Delivery

async sendSingleNotification(payload: NotificationPayload, token: string): Promise<string> {
const response = await this.messaging.send({
notification: {
title: payload.subject,
body: payload.description,
},
token,
});

// Error handling and logging
return response;
}

Bulk Notification Delivery

async sendBulkNotification(payload: MulticastNotificationPayload, tokens: string[]): Promise<BatchResponse> {
const response = await this.messaging.sendEachForMulticast({
data: {
screen_type: payload.custom_data?.screen_type || ScreenTypeEnum.HOME_SCREEN,
},
notification: {
title: payload.subject,
body: payload.description,
},
tokens,
});

// Process failed tokens and responses
return response;
}

Error Handling & Token Cleanup

// Automatic cleanup of invalid tokens
if (notificationResponse.failureCount > 0) {
const expiredTokens = notificationResponse.responses
.filter(response => {
return response.error?.code === 'messaging/invalid-registration-token' ||
response.error?.code === 'messaging/registration-token-not-registered';
})
.map((_, index) => tokens[index]);

await this.removeDeviceTokens(expiredTokens);
}

Topic-based Notifications

async sendNotificationToTopic(topic: string, payload: NotificationPayload) {
const response = await this.messaging.send({
notification: {
title: payload.subject,
body: payload.description,
},
topic: topic,
});

return response;
}

Background Job Processing

Job Queue Configuration

@Processor(QueueName.NOTIFICATION, {
concurrency: 2, // Process 2 jobs simultaneously
drainDelay: 300, // Wait time before shutdown
stalledInterval: 300000, // 5 minutes stalled timeout
maxStalledCount: 3, // Max stalled attempts
limiter: {
max: 10, // Max 10 jobs
duration: 150, // Per 150ms
},
})

Job Types & Processing

Custom Notification Job

case JobName.SEND_CUSTOM_NOTIFICATION:
await this.notificationQueueService.sendCustomNotification({
user_ids: ['user-uuid'],
subject: 'New Assessment Available',
description: 'A new assessment has been assigned',
custom_data: { screen_type: 'ASSESSMENT_SCREEN' }
});
break;

Template Notification Job

case JobName.NOTIFICATION_SEND:
await this.notificationQueueService.sendNotification({
user_ids: ['user-uuid'],
notification_template_ids: ['template-uuid']
});
break;

Notification Logging Job

case JobName.SEND_NOTIFICATION_LOG:
await this.notificationQueueService.sendNotificationLog({
notification_template_id: 'template-uuid',
sent_at: new Date(),
user_ids: ['user-uuid'],
sent_by: 'admin-uuid'
});
break;

Error Handling & Recovery

@OnWorkerEvent('failed')
async onFailed(job: Job) {
// Log failure details
this.logger.error(`Job ${job.id} failed: ${job.failedReason}`);

// Move to Dead Letter Queue for analysis
await this.dlqService.addFailedJobToDLQ({
originalQueueName: QueueName.NOTIFICATION,
originalJobId: job.id,
originalJobData: job.data,
failedReason: job.failedReason,
timestamp: Date.now(),
});
}

Device Token Management

Token Registration Process

App Install/Login → FCM Token Generated → Token Sent to Backend → Database Storage → Token Validation → Ready for Notifications

Token Lifecycle Management

// Token storage with user association
{
user_id: "user-uuid",
device_token: "fcm-token-string",
platform: "iOS|Android|Web",
app_version: "1.0.0",
created_at: "timestamp",
last_used: "timestamp"
}

Automatic Token Cleanup

// Remove expired and invalid tokens
async removeDeviceTokens(tokens: string[]): Promise<void> {
// Delete from database
await this.notificationsDBService.deleteDeviceTokens(tokens);

// Log cleanup activity
this.logger.debug(`Removed ${tokens.length} expired device tokens`);
}

Token Validation Logic

  • Registration Token Not Registered - Token removed from database
  • Invalid Registration Token - Token marked as invalid and removed
  • Message Rate Exceeded - Temporary backoff applied
  • Server Unavailable - Retry with exponential backoff

Template System

Notification Templates

Predefined notification templates stored in database with variables support:

// Template structure
{
id: "template-uuid",
subject: "New Assessment Available",
description: "A new {{assessment_type}} assessment has been assigned to {{child_name}}",
deep_link: "ASSESSMENT_SCREEN",
status: "ACTIVE",
variables: ["assessment_type", "child_name"]
}

Template Categories

  • System Notifications - App updates, maintenance alerts
  • Assessment Notifications - New assessments, completion reminders
  • Appointment Notifications - Booking confirmations, reminders
  • Progress Notifications - Milestone achievements, reports
  • Marketing Notifications - Feature announcements, promotions

Variable Substitution

// Template with variables
const template = "Hello {{parent_name}}, {{child_name}} has completed the {{assessment_name}} assessment";

// Runtime substitution
const notification = template
.replace('{{parent_name}}', user.name)
.replace('{{child_name}}', child.name)
.replace('{{assessment_name}}', assessment.name);

Security & Authorization

Access Control

OperationADMINTHERAPISTPARENTPUBLIC
Send Template Notifications
Send Custom Notifications
Send Single Notifications
View Notification Logs
Manage Templates

Firebase Security

  • Service Account Authentication - Server-to-server authentication
  • Token Validation - Device token validation before sending
  • Rate Limiting - Prevents abuse and API quota violations
  • Error Logging - Comprehensive error logging for security monitoring

Data Protection

  • Token Encryption - Device tokens encrypted at rest
  • Content Filtering - Notification content validated and sanitized
  • User Isolation - Users can only receive notifications intended for them
  • Audit Trails - All notification activities logged for compliance

Technical Implementation

Service Architecture

@Injectable()
export class NotificationsService {
constructor(
@Inject('FirebaseProvider') private readonly firebaseProvider: FirebaseProvider,
private readonly notificationsDBService: NotificationsDBService,
private readonly backgroundServiceManager: BackgroundServiceManager
) {}

async sendCustomNotification(data: MulticastNotificationPayload, user_ids: string[]) {
// Fetch device tokens for target users
const tokens = await this.getDeviceTokens(user_ids);

// Save in-app notifications to database
await this.saveUserNotification(notificationEntries);

// Send push notifications via Firebase
if (tokens.length > 0) {
const response = await this.firebaseProvider.sendBulkNotification(data, tokens);

// Handle failed tokens
await this.handleFailedTokens(response, tokens);
}
}
}

Firebase Provider Implementation

@Injectable()
export class FirebaseProvider implements NotificationProvider {
private readonly messaging: admin.messaging.Messaging;

constructor(private readonly configService: ConfigService<EnvConfig>) {
// Initialize Firebase Admin SDK
this.firebaseAdmin = admin.initializeApp({
credential: admin.credential.cert({
projectId: this.configService.get('FIREBASE_PROJECT_ID'),
clientEmail: this.configService.get('FIREBASE_CLIENT_EMAIL'),
privateKey: this.configService.get('FIREBASE_PRIVATE_KEY'),
}),
});

this.messaging = getMessaging();
}
}

Background Job Integration

// Queue notification jobs for async processing
await this.backgroundServiceManager.addSendCustomNotificationJob(JobName.SEND_CUSTOM_NOTIFICATION, {
user_ids: targetUsers,
subject: 'Assessment Completed',
description: `${childName} has completed the ${assessmentName} assessment`,
custom_data: {
screen_type: 'ASSESSMENT_RESULTS',
assessment_id: assessmentId
}
});

Error Handling Strategy

// Comprehensive error handling
try {
const response = await this.firebaseProvider.sendBulkNotification(payload, tokens);

// Process delivery results
if (response.failureCount > 0) {
const failedTokens = this.extractFailedTokens(response);
await this.removeDeviceTokens(failedTokens);
}

} catch (error) {
// Log error details
this.logger.error(`Notification delivery failed: ${error.message}`);

// Queue for retry if applicable
if (this.isRetryableError(error)) {
await this.queueForRetry(payload, tokens);
}

throw error;
}

Performance Optimizations

  • Batch Processing - Send notifications in batches to optimize Firebase API usage
  • Token Caching - Cache frequently used device tokens
  • Async Processing - Use background jobs for non-critical notification delivery
  • Connection Pooling - Reuse Firebase connections for efficiency
  • Rate Limiting - Implement rate limiting to prevent API quota exhaustion

Monitoring & Analytics

  • Delivery Metrics - Track success/failure rates for notifications
  • Performance Monitoring - Monitor delivery times and error rates
  • User Engagement - Track notification open rates and user interactions
  • System Health - Monitor queue depths and processing times

This Notifications module provides a robust, scalable push notification system that ensures reliable message delivery across the Comdeall platform, with comprehensive error handling, automatic token management, and efficient background processing for optimal user experience.