+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 317 of 355

๐Ÿ“˜ Notification System: Email and Push

Master notification system: email and push in TypeScript with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript ๐Ÿ“
  • TypeScript installation โšก
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand the concept fundamentals ๐ŸŽฏ
  • Apply the concept in real projects ๐Ÿ—๏ธ
  • Debug common issues ๐Ÿ›
  • Write type-safe code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on building notification systems! ๐ŸŽ‰ In this guide, weโ€™ll explore how to create robust email and push notification systems using TypeScript.

Youโ€™ll discover how proper notification architecture can transform your applicationโ€™s user engagement. Whether youโ€™re building social media platforms ๐Ÿ“ฑ, e-commerce sites ๐Ÿ›’, or SaaS applications ๐Ÿ’ผ, understanding notification systems is essential for keeping users informed and engaged.

By the end of this tutorial, youโ€™ll feel confident implementing notification systems in your own projects! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Notification Systems

๐Ÿค” What is a Notification System?

A notification system is like a smart postal service ๐Ÿ“ฎ. Think of it as your applicationโ€™s way of sending messages to users through different channels (email, push notifications, SMS) to keep them informed about important events.

In TypeScript terms, a notification system is a structured way to:

  • โœจ Send messages across multiple channels
  • ๐Ÿš€ Queue and batch notifications efficiently
  • ๐Ÿ›ก๏ธ Handle failures and retries gracefully

๐Ÿ’ก Why Use TypeScript for Notifications?

Hereโ€™s why developers love TypeScript for notification systems:

  1. Type Safety ๐Ÿ”’: Ensure notification payloads are always correct
  2. Better IDE Support ๐Ÿ’ป: Autocomplete for notification templates
  3. Code Documentation ๐Ÿ“–: Types serve as inline documentation
  4. Refactoring Confidence ๐Ÿ”ง: Change notification structures safely

Real-world example: Imagine building a social media app ๐Ÿ“ฑ. With TypeScript, you can ensure every โ€œlikeโ€ notification has the correct user data, post reference, and timestamp.

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Simple Email Notification

Letโ€™s start with a friendly example:

// ๐Ÿ‘‹ Hello, Notifications!
interface EmailNotification {
  to: string;          // ๐Ÿ“ง Recipient email
  subject: string;     // ๐Ÿ“ Email subject
  body: string;        // ๐Ÿ’ฌ Email content
  priority?: 'low' | 'normal' | 'high';  // ๐ŸŽฏ Optional priority
}

// ๐ŸŽจ Creating a notification service
class NotificationService {
  private emailQueue: EmailNotification[] = [];
  
  // ๐Ÿ“ฎ Send email notification
  async sendEmail(notification: EmailNotification): Promise<void> {
    console.log(`๐Ÿ“ง Sending email to ${notification.to}`);
    // Email sending logic here
  }
}

๐ŸŽฏ Push Notification Pattern

Hereโ€™s how to structure push notifications:

// ๐Ÿ”” Push notification interface
interface PushNotification {
  userId: string;      // ๐Ÿ‘ค Target user
  title: string;       // ๐Ÿ“Œ Notification title
  message: string;     // ๐Ÿ’ฌ Notification body
  icon?: string;       // ๐ŸŽจ Optional icon URL
  badge?: number;      // ๐Ÿ”ข Optional badge count
  data?: Record<string, any>;  // ๐Ÿ“ฆ Custom data payload
}

// ๐Ÿ“ฑ Device token management
interface DeviceToken {
  userId: string;
  token: string;
  platform: 'ios' | 'android' | 'web';
  lastActive: Date;
}

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Order Notifications

Letโ€™s build a real notification system for an online store:

// ๐Ÿ›๏ธ Order notification types
interface OrderNotification {
  orderId: string;
  customerEmail: string;
  customerName: string;
  items: Array<{
    name: string;
    quantity: number;
    emoji: string;  // Every product needs an emoji!
  }>;
  total: number;
  status: 'confirmed' | 'shipped' | 'delivered';
}

// ๐Ÿ“ง Email template builder
class OrderEmailBuilder {
  // ๐ŸŽจ Build order confirmation email
  buildConfirmationEmail(order: OrderNotification): EmailNotification {
    const itemsList = order.items
      .map(item => `${item.emoji} ${item.name} x${item.quantity}`)
      .join('\n');
    
    return {
      to: order.customerEmail,
      subject: `๐ŸŽ‰ Order Confirmed! #${order.orderId}`,
      body: `
        Hi ${order.customerName}! ๐Ÿ‘‹
        
        Your order has been confirmed! ๐ŸŽŠ
        
        ๐Ÿ“ฆ Order Details:
        ${itemsList}
        
        ๐Ÿ’ฐ Total: $${order.total}
        
        We'll notify you when it ships! ๐Ÿšš
      `,
      priority: 'high'
    };
  }
  
  // ๐Ÿšš Build shipping notification
  buildShippingEmail(order: OrderNotification): EmailNotification {
    return {
      to: order.customerEmail,
      subject: `๐Ÿšš Your order is on the way! #${order.orderId}`,
      body: `Great news ${order.customerName}! Your order is shipped! ๐Ÿ“ฆโœจ`,
      priority: 'normal'
    };
  }
}

// ๐Ÿ”” Push notification builder
class OrderPushBuilder {
  buildOrderPush(order: OrderNotification): PushNotification {
    const statusEmojis = {
      confirmed: 'โœ…',
      shipped: '๐Ÿšš',
      delivered: '๐Ÿ“ฆ'
    };
    
    return {
      userId: order.customerEmail,  // In real app, use userId
      title: `${statusEmojis[order.status]} Order ${order.status}!`,
      message: `Your order #${order.orderId} is ${order.status}`,
      badge: 1,
      data: {
        orderId: order.orderId,
        deepLink: `/orders/${order.orderId}`
      }
    };
  }
}

// ๐ŸŽฎ Let's use it!
const emailBuilder = new OrderEmailBuilder();
const pushBuilder = new OrderPushBuilder();
const notificationService = new NotificationService();

const order: OrderNotification = {
  orderId: "12345",
  customerEmail: "[email protected]",
  customerName: "Sarah",
  items: [
    { name: "TypeScript Book", quantity: 1, emoji: "๐Ÿ“˜" },
    { name: "Coffee Mug", quantity: 2, emoji: "โ˜•" }
  ],
  total: 49.99,
  status: 'confirmed'
};

// Send notifications
const email = emailBuilder.buildConfirmationEmail(order);
await notificationService.sendEmail(email);

๐ŸŽฎ Example 2: Social Media Engagement Notifications

Letโ€™s create a notification system for social interactions:

// ๐ŸŒŸ Social notification types
type NotificationType = 
  | 'like' 
  | 'comment' 
  | 'follow' 
  | 'mention' 
  | 'share';

interface SocialNotification {
  id: string;
  recipientId: string;
  actorName: string;
  actorAvatar: string;
  type: NotificationType;
  targetId: string;  // Post ID, comment ID, etc.
  timestamp: Date;
  read: boolean;
}

// ๐Ÿ“Š Notification aggregator
class NotificationAggregator {
  private notifications = new Map<string, SocialNotification[]>();
  
  // ๐ŸŽฏ Add notification with smart batching
  addNotification(notification: SocialNotification): void {
    const key = `${notification.recipientId}-${notification.type}-${notification.targetId}`;
    
    if (!this.notifications.has(key)) {
      this.notifications.set(key, []);
    }
    
    this.notifications.get(key)!.push(notification);
  }
  
  // ๐Ÿ“ฆ Get batched notifications
  getBatchedNotifications(userId: string): Array<{
    type: NotificationType;
    count: number;
    actors: string[];
    message: string;
    emoji: string;
  }> {
    const userNotifications: any[] = [];
    
    this.notifications.forEach((notifs, key) => {
      if (key.startsWith(userId)) {
        const type = notifs[0].type;
        const actors = [...new Set(notifs.map(n => n.actorName))];
        
        userNotifications.push({
          type,
          count: notifs.length,
          actors: actors.slice(0, 3),
          message: this.buildMessage(type, actors, notifs.length),
          emoji: this.getEmoji(type)
        });
      }
    });
    
    return userNotifications;
  }
  
  // ๐Ÿ’ฌ Build aggregated message
  private buildMessage(
    type: NotificationType, 
    actors: string[], 
    count: number
  ): string {
    const othersCount = count - actors.length;
    const actorString = actors.join(', ');
    const others = othersCount > 0 ? ` and ${othersCount} others` : '';
    
    const messages = {
      like: `liked your post`,
      comment: `commented on your post`,
      follow: `started following you`,
      mention: `mentioned you`,
      share: `shared your post`
    };
    
    return `${actorString}${others} ${messages[type]}`;
  }
  
  // ๐ŸŽจ Get emoji for notification type
  private getEmoji(type: NotificationType): string {
    const emojis = {
      like: 'โค๏ธ',
      comment: '๐Ÿ’ฌ',
      follow: '๐Ÿ‘ฅ',
      mention: '@',
      share: '๐Ÿ”„'
    };
    return emojis[type];
  }
}

// ๐Ÿ”” Real-time notification dispatcher
class RealtimeNotificationDispatcher {
  private subscribers = new Map<string, (notification: any) => void>();
  
  // ๐Ÿ“ก Subscribe to notifications
  subscribe(userId: string, callback: (notification: any) => void): void {
    this.subscribers.set(userId, callback);
    console.log(`๐Ÿ‘ค ${userId} subscribed to notifications`);
  }
  
  // ๐Ÿ“ค Dispatch notification
  dispatch(notification: SocialNotification): void {
    const callback = this.subscribers.get(notification.recipientId);
    if (callback) {
      callback({
        ...notification,
        formattedTime: this.formatTime(notification.timestamp)
      });
    }
  }
  
  // โฐ Format time for display
  private formatTime(date: Date): string {
    const now = new Date();
    const diff = now.getTime() - date.getTime();
    const minutes = Math.floor(diff / 60000);
    
    if (minutes < 1) return 'just now โšก';
    if (minutes < 60) return `${minutes}m ago ๐Ÿ•`;
    if (minutes < 1440) return `${Math.floor(minutes / 60)}h ago ๐Ÿ•‘`;
    return `${Math.floor(minutes / 1440)}d ago ๐Ÿ“…`;
  }
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Notification Queuing

When youโ€™re ready to level up, implement advanced queuing:

// ๐ŸŽฏ Advanced notification queue with retry logic
interface QueuedNotification<T> {
  id: string;
  payload: T;
  channel: 'email' | 'push' | 'sms';
  attempts: number;
  maxAttempts: number;
  nextRetry?: Date;
  priority: number;
  status: 'pending' | 'processing' | 'completed' | 'failed';
}

// ๐Ÿช„ Priority queue implementation
class NotificationQueue<T> {
  private queue: QueuedNotification<T>[] = [];
  private processing = false;
  
  // โž• Add to queue with priority
  enqueue(
    notification: T, 
    channel: QueuedNotification<T>['channel'],
    priority: number = 5
  ): void {
    const queued: QueuedNotification<T> = {
      id: Date.now().toString(),
      payload: notification,
      channel,
      attempts: 0,
      maxAttempts: 3,
      priority,
      status: 'pending'
    };
    
    // Insert based on priority
    const insertIndex = this.queue.findIndex(n => n.priority < priority);
    if (insertIndex === -1) {
      this.queue.push(queued);
    } else {
      this.queue.splice(insertIndex, 0, queued);
    }
    
    this.processQueue();
  }
  
  // ๐Ÿ”„ Process queue with exponential backoff
  private async processQueue(): Promise<void> {
    if (this.processing || this.queue.length === 0) return;
    
    this.processing = true;
    const notification = this.queue.shift()!;
    
    try {
      notification.status = 'processing';
      await this.sendNotification(notification);
      notification.status = 'completed';
      console.log(`โœ… Sent ${notification.channel} notification`);
    } catch (error) {
      notification.attempts++;
      if (notification.attempts < notification.maxAttempts) {
        notification.status = 'pending';
        notification.nextRetry = new Date(
          Date.now() + Math.pow(2, notification.attempts) * 1000
        );
        this.queue.push(notification);
        console.log(`๐Ÿ”„ Retry scheduled for ${notification.id}`);
      } else {
        notification.status = 'failed';
        console.log(`โŒ Failed to send ${notification.id}`);
      }
    }
    
    this.processing = false;
    setTimeout(() => this.processQueue(), 100);
  }
  
  // ๐Ÿ“ค Send notification (implement based on channel)
  private async sendNotification(
    notification: QueuedNotification<T>
  ): Promise<void> {
    // Simulate sending
    await new Promise(resolve => setTimeout(resolve, 1000));
    if (Math.random() > 0.8) {
      throw new Error('Simulated failure');
    }
  }
}

๐Ÿ—๏ธ Advanced Topic 2: Template System

For the brave developers, hereโ€™s a template system:

// ๐Ÿš€ Type-safe template system
type TemplateVars<T extends string> = 
  T extends `${infer _Start}{{${infer Var}}}${infer Rest}`
    ? Var | TemplateVars<Rest>
    : never;

type TemplateData<T extends string> = Record<TemplateVars<T>, string>;

// ๐ŸŽจ Template engine
class NotificationTemplateEngine {
  private templates = new Map<string, string>();
  
  // ๐Ÿ“ Register template
  registerTemplate<T extends string>(
    name: string, 
    template: T
  ): void {
    this.templates.set(name, template);
  }
  
  // ๐Ÿ”„ Render template with type safety
  render<T extends string>(
    templateName: string,
    data: TemplateData<T>
  ): string {
    let template = this.templates.get(templateName) || '';
    
    Object.entries(data).forEach(([key, value]) => {
      template = template.replace(
        new RegExp(`{{${key}}}`, 'g'),
        value as string
      );
    });
    
    return template;
  }
}

// ๐ŸŽฏ Usage with type safety
const engine = new NotificationTemplateEngine();
engine.registerTemplate(
  'welcome',
  'Welcome {{name}}! ๐ŸŽ‰ Your account {{email}} is ready.'
);

// TypeScript ensures all variables are provided!
const rendered = engine.render('welcome', {
  name: 'Alice',
  email: '[email protected]'
});

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Notification Spam

// โŒ Wrong way - sending too many notifications!
users.forEach(user => {
  notifications.forEach(notification => {
    sendNotification(user, notification); // ๐Ÿ’ฅ Spam alert!
  });
});

// โœ… Correct way - batch and throttle!
class NotificationThrottler {
  private lastSent = new Map<string, Date>();
  private minInterval = 60000; // 1 minute
  
  canSend(userId: string): boolean {
    const last = this.lastSent.get(userId);
    if (!last) return true;
    
    return Date.now() - last.getTime() > this.minInterval;
  }
  
  markSent(userId: string): void {
    this.lastSent.set(userId, new Date());
  }
}

๐Ÿคฏ Pitfall 2: Missing Error Handling

// โŒ Dangerous - no error handling!
async function sendPushNotification(token: string, message: string) {
  const response = await fetch('/api/push', {
    method: 'POST',
    body: JSON.stringify({ token, message })
  });
  // ๐Ÿ’ฅ What if this fails?
}

// โœ… Safe - proper error handling!
async function sendPushNotification(
  token: string, 
  message: string
): Promise<{ success: boolean; error?: string }> {
  try {
    const response = await fetch('/api/push', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token, message })
    });
    
    if (!response.ok) {
      console.log(`โš ๏ธ Push failed: ${response.status}`);
      return { success: false, error: response.statusText };
    }
    
    return { success: true };
  } catch (error) {
    console.log(`โŒ Push error: ${error}`);
    return { success: false, error: String(error) };
  }
}

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Queues: Always queue notifications for reliability
  2. ๐Ÿ“ Template Everything: Use templates for consistent messaging
  3. ๐Ÿ›ก๏ธ Handle Failures: Implement retry logic with backoff
  4. ๐ŸŽจ Batch Similar Notifications: Aggregate to reduce noise
  5. โœจ Respect User Preferences: Always check notification settings

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Complete Notification System

Create a notification system for a task management app:

๐Ÿ“‹ Requirements:

  • โœ… Support email and push notifications
  • ๐Ÿท๏ธ Different notification types (task assigned, due soon, completed)
  • ๐Ÿ‘ค User preference management
  • ๐Ÿ“… Schedule notifications for future delivery
  • ๐ŸŽจ Each notification type needs custom templates!

๐Ÿš€ Bonus Points:

  • Add SMS support
  • Implement notification history
  • Create an unsubscribe system

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Complete notification system for task management!
interface Task {
  id: string;
  title: string;
  assigneeId: string;
  dueDate: Date;
  priority: 'low' | 'medium' | 'high';
  emoji: string;
}

interface UserPreferences {
  userId: string;
  email: boolean;
  push: boolean;
  sms: boolean;
  quietHours: { start: number; end: number };
  frequency: 'instant' | 'hourly' | 'daily';
}

// ๐Ÿ“ง Notification service
class TaskNotificationService {
  private preferences = new Map<string, UserPreferences>();
  private scheduled = new Map<string, Date>();
  
  // ๐ŸŽฏ Send task assigned notification
  async notifyTaskAssigned(task: Task, assignerName: string): Promise<void> {
    const prefs = this.preferences.get(task.assigneeId);
    if (!prefs) return;
    
    const notification = {
      title: `${task.emoji} New task assigned!`,
      message: `${assignerName} assigned you: ${task.title}`,
      priority: task.priority,
      data: { taskId: task.id }
    };
    
    if (this.canSendNow(prefs)) {
      if (prefs.email) await this.sendEmail(task.assigneeId, notification);
      if (prefs.push) await this.sendPush(task.assigneeId, notification);
    } else {
      this.scheduleForLater(task.assigneeId, notification);
    }
  }
  
  // โฐ Check quiet hours
  private canSendNow(prefs: UserPreferences): boolean {
    const hour = new Date().getHours();
    const { start, end } = prefs.quietHours;
    
    if (start < end) {
      return hour < start || hour >= end;
    } else {
      return hour < start && hour >= end;
    }
  }
  
  // ๐Ÿ“… Schedule for later
  private scheduleForLater(userId: string, notification: any): void {
    const prefs = this.preferences.get(userId)!;
    const now = new Date();
    const hour = now.getHours();
    const { end } = prefs.quietHours;
    
    let scheduledHour = end;
    if (hour < end) {
      scheduledHour = end;
    } else {
      now.setDate(now.getDate() + 1);
      scheduledHour = end;
    }
    
    const scheduled = new Date(now);
    scheduled.setHours(scheduledHour, 0, 0, 0);
    
    this.scheduled.set(`${userId}-${Date.now()}`, scheduled);
    console.log(`๐Ÿ“… Scheduled notification for ${scheduled.toISOString()}`);
  }
  
  // ๐Ÿ“Š Get notification stats
  getStats(userId: string): {
    sent: number;
    scheduled: number;
    preferences: UserPreferences | undefined;
  } {
    const scheduled = Array.from(this.scheduled.keys())
      .filter(key => key.startsWith(userId)).length;
    
    return {
      sent: 0, // Would track this in real implementation
      scheduled,
      preferences: this.preferences.get(userId)
    };
  }
  
  // Stub methods for sending
  private async sendEmail(userId: string, notification: any): Promise<void> {
    console.log(`๐Ÿ“ง Email sent to ${userId}: ${notification.title}`);
  }
  
  private async sendPush(userId: string, notification: any): Promise<void> {
    console.log(`๐Ÿ”” Push sent to ${userId}: ${notification.title}`);
  }
}

// ๐ŸŽฎ Test it out!
const notificationService = new TaskNotificationService();

// Set user preferences
notificationService['preferences'].set('user123', {
  userId: 'user123',
  email: true,
  push: true,
  sms: false,
  quietHours: { start: 22, end: 8 },
  frequency: 'instant'
});

// Send notification
await notificationService.notifyTaskAssigned({
  id: 'task456',
  title: 'Review TypeScript PR',
  assigneeId: 'user123',
  dueDate: new Date(),
  priority: 'high',
  emoji: '๐Ÿ‘จโ€๐Ÿ’ป'
}, 'Alice');

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Build notification systems with confidence ๐Ÿ’ช
  • โœ… Implement multiple channels (email, push, SMS) ๐Ÿ“ฑ
  • โœ… Handle queuing and retries like a pro ๐Ÿ”„
  • โœ… Respect user preferences and quiet hours ๐ŸŒ™
  • โœ… Create scalable architectures with TypeScript! ๐Ÿš€

Remember: Good notification systems enhance user experience without being annoying! ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered notification systems!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Build the exercise notification system
  2. ๐Ÿ—๏ธ Add real email/push providers (SendGrid, Firebase)
  3. ๐Ÿ“š Learn about notification analytics
  4. ๐ŸŒŸ Implement A/B testing for notification content

Remember: Every great app has a thoughtful notification system. Keep building, keep notifying wisely! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿš€โœจ