How to implement real-time notifications in Node.js

Real-time notifications keep users informed of important events without requiring page refreshes or polling, creating responsive and engaging applications. As the creator of CoreUI with over 12 years of Node.js experience since 2014, I’ve implemented notification systems for numerous enterprise dashboards. A real-time notification system uses WebSockets to maintain persistent connections and push notifications instantly when events occur. This approach provides immediate user feedback for actions like new messages, status updates, or system alerts.

Create a notification service using WebSockets to push real-time updates to authenticated users.

const WebSocket = require('ws')
const http = require('http')

class NotificationService {
  constructor(server) {
    this.wss = new WebSocket.Server({ server })
    this.userConnections = new Map() // userId -> Set of WebSocket connections

    this.wss.on('connection', (ws, req) => {
      this.handleConnection(ws, req)
    })
  }

  handleConnection(ws, req) {
    let userId = null

    ws.on('message', (data) => {
      try {
        const message = JSON.parse(data)

        // Authenticate user
        if (message.type === 'auth') {
          userId = message.userId // In production, validate token
          this.registerUser(userId, ws)

          ws.send(JSON.stringify({
            type: 'auth_success',
            message: 'Connected to notification service'
          }))
        }
      } catch (error) {
        console.error('Error handling message:', error)
      }
    })

    ws.on('close', () => {
      if (userId) {
        this.unregisterUser(userId, ws)
      }
    })
  }

  registerUser(userId, ws) {
    if (!this.userConnections.has(userId)) {
      this.userConnections.set(userId, new Set())
    }
    this.userConnections.get(userId).add(ws)
    console.log(`User ${userId} connected. Total connections: ${this.userConnections.get(userId).size}`)
  }

  unregisterUser(userId, ws) {
    const connections = this.userConnections.get(userId)
    if (connections) {
      connections.delete(ws)
      if (connections.size === 0) {
        this.userConnections.delete(userId)
      }
    }
  }

  sendToUser(userId, notification) {
    const connections = this.userConnections.get(userId)
    if (!connections) return

    const message = JSON.stringify({
      type: 'notification',
      ...notification,
      timestamp: new Date().toISOString()
    })

    connections.forEach(ws => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(message)
      }
    })
  }

  sendToMultipleUsers(userIds, notification) {
    userIds.forEach(userId => {
      this.sendToUser(userId, notification)
    })
  }

  broadcast(notification) {
    const message = JSON.stringify({
      type: 'notification',
      ...notification,
      timestamp: new Date().toISOString()
    })

    this.userConnections.forEach((connections) => {
      connections.forEach(ws => {
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(message)
        }
      })
    })
  }
}

// Usage example
const server = http.createServer()
const notificationService = new NotificationService(server)

// Simulate sending notifications
setInterval(() => {
  notificationService.sendToUser('user123', {
    title: 'New Message',
    body: 'You have received a new message',
    priority: 'high'
  })
}, 10000)

// Broadcast to all users
notificationService.broadcast({
  title: 'System Maintenance',
  body: 'Scheduled maintenance in 1 hour',
  priority: 'medium'
})

server.listen(3000, () => {
  console.log('Notification service running on port 3000')
})

Client-side usage:

const ws = new WebSocket('ws://localhost:3000')

ws.onopen = () => {
  // Authenticate
  ws.send(JSON.stringify({
    type: 'auth',
    userId: 'user123',
    token: 'your-auth-token'
  }))
}

ws.onmessage = (event) => {
  const data = JSON.parse(event.data)

  if (data.type === 'notification') {
    showNotification(data.title, data.body, data.priority)
  }
}

function showNotification(title, body, priority) {
  // Display notification in UI
  console.log(`[${priority}] ${title}: ${body}`)
}

Best Practice Note

Store user-to-connection mappings to efficiently route notifications to specific users. Support multiple connections per user for users with multiple browser tabs. Implement exponential backoff for automatic reconnection on disconnect. Queue notifications if the user is offline and deliver when they reconnect. Add notification persistence with a database for notification history. This is how we build notification systems for CoreUI dashboards—using WebSockets for instant delivery with proper user authentication, connection management, and graceful error handling.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to check if an element is visible in JavaScript
How to check if an element is visible in JavaScript

How to loop inside React JSX
How to loop inside React JSX

Passing props to child components in React function components
Passing props to child components in React function components

How to capitalize the first letter in JavaScript?
How to capitalize the first letter in JavaScript?

Answers by CoreUI Core Team