100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Real-Time Notifications

Design a reliable real-time notification system with Socket.IO using per-user rooms, namespaces, and durable fan-out for offline users.

Practical Socket.IOIntermediate8 min readJul 10, 2026
Analogies

Real-Time Notifications with Socket.IO

Real-time notifications differ from chat in one key way: the recipient is usually not actively viewing the sender's context, so the server must be able to push to a specific user regardless of which page or room they're on. The common pattern is a per-user room — on connection, the server calls socket.join('user:' + userId), and any backend service that needs to notify that user later simply does io.to('user:' + userId).emit('notification:new', payload), decoupling the notification trigger (e.g. a comment being posted) from the delivery mechanism.

🏏

Cricket analogy: It's like a fan's personalized alert for their favorite player — instead of blasting every score update to all app users, the system only pings you when Virat Kohli specifically reaches a milestone, because you subscribed to his name, not the whole match feed.

Namespaces vs. Rooms for Notification Scoping

Beyond per-user rooms, larger systems often introduce a dedicated namespace such as /notifications, created with io.of('/notifications'), which opens a logically separate connection with its own middleware and event handlers while still sharing the same underlying transport as the main namespace. This is useful when notification traffic has different authentication requirements or a different client bundle (e.g. a lightweight notification widget embedded on marketing pages) than the primary application namespace, whereas rooms remain the right tool for grouping within a namespace, such as grouping all admins who should see moderation alerts.

🏏

Cricket analogy: It's like the difference between a stadium having two entirely separate broadcast rights holders (namespaces) for domestic and international feeds, versus that same broadcaster having separate camera angles (rooms) for the pavilion end and the members' stand.

Handling Offline Users and Notification Fan-Out

A user who is disconnected when a notification fires will simply never receive the io.to('user:'+userId).emit(...) call — Socket.IO does not queue events for offline sockets — so production systems pair the live emit with a durable notification record written to the database in the same code path, and the client fetches unread notifications via a normal REST call on reconnect or app load to reconcile anything missed while offline. This fan-out pattern (write to DB, then attempt live emit, and separately trigger push notifications or email for users who are offline for extended periods) is what makes notification systems reliable rather than merely fast.

🏏

Cricket analogy: It's like a stadium's LED perimeter board announcing a boundary — if you stepped out to the concourse for a snack at that exact moment, you missed it live, so the scoreboard app on your phone (the durable record) is what you check when you're back to catch up.

javascript
// notifications namespace and per-user rooms
const notifyNsp = io.of('/notifications');

notifyNsp.use(authenticateSocket); // verifies JWT, sets socket.data.userId

notifyNsp.on('connection', (socket) => {
  socket.join(`user:${socket.data.userId}`);

  socket.on('notification:markRead', async ({ notificationId }) => {
    await Notification.updateOne({ _id: notificationId }, { read: true });
  });
});

// Elsewhere in the app, e.g. after a comment is created:
async function notifyUser(userId, payload) {
  const record = await Notification.create({ userId, ...payload, read: false });
  notifyNsp.to(`user:${userId}`).emit('notification:new', record);
  return record;
}

On reconnect, always have the client call a REST endpoint like GET /api/notifications?unread=true to reconcile state. This guarantees correctness even if the live emit was missed entirely, whereas relying on Socket.IO alone provides only best-effort, at-most-once delivery.

  • Per-user rooms (user:<id>) let any backend service push a notification to a specific user regardless of what page they're on.
  • Dedicated namespaces (io.of('/notifications')) separate notification traffic's auth and event handlers from the main app namespace.
  • Socket.IO never queues events for a disconnected socket — an offline user simply misses the live emit.
  • Always persist a durable notification record to the database alongside the live emit so the client can reconcile on reconnect.
  • Fan-out for critical notifications should include a fallback channel (push notification or email) for users offline for extended periods.
  • Rooms group members within a namespace (e.g. all admins); namespaces separate entire connection contexts with different auth needs.
  • Client-side reconciliation via a REST 'unread notifications' endpoint is what makes the system reliable, not just fast.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#SocketIOStudyNotes#RealTimeNotifications#Real#Time#Notifications#Socket#StudyNotes#SkillVeris#ExamPrep