DEV Community

Cover image for Real-Time Notifications in MERN using Socket.IO
Suriya Prakash
Suriya Prakash

Posted on

Real-Time Notifications in MERN using Socket.IO

Real-time notifications are a must for modern applications—whether it’s admin alerts, user signups, or background job updates.

In this article, I’ll walk through how to implement Socket.IO notifications in a MERN stack, using a real production-style backend setup.

🧱 Tech Stack

  • MongoDB
  • Express.js
  • React.js
  • Node.js
  • Socket.IO
  • node-cron (for scheduled background jobs)

📌 Use Case

  • Notify admins when a new user signs up
  • Send real-time alerts when a technician’s work exceeds 2 hours
  • Persist notifications in MongoDB
  • Push notifications instantly to connected clients

⚙️ Server Setup (Socket.IO + Express)

First, we create an HTTP server and attach Socket.IO to it.

const http = require("http");
const { Server } = require("socket.io");

const server = http.createServer(app);

const io = new Server(server, {
  cors: {
    origin: "*",
  },
});

app.set("io", io);
Enter fullscreen mode Exit fullscreen mode

Why app.set('io', io)?
This allows us to access the Socket.IO instance anywhere in our app, including controllers and services.

🧩 Express Middleware & Routes
All normal Express middleware still works:

app.use(express.json());
app.use(cors());
app.use(helmet());
app.use("/api/admin", adminRouter);
Enter fullscreen mode Exit fullscreen mode

Socket.IO runs alongside your REST APIs—not instead of them.

🔔 Emitting Notifications on User Signup
When a user completes signup, we:

  1. Save the notification in MongoDB
  2. Emit a real-time event via Socket.IO

Signup Controller (Important Part)

const io = req.app.get('io');

const notification = await Notification.create({
  type: 'signup',
  message: `New user registered: ${user.basicInfo.fullName}`,
  userId: user._id,
  time: new Date(),
  read: false
});

io.emit('notification', notification);
Enter fullscreen mode Exit fullscreen mode

What’s happening here?

  • io.emit() broadcasts to all connected clients
  • You can later replace this with:
    • io.to(userId).emit() (user-specific)
    • io.to("admin").emit() (role-based rooms)

⏱ Background Notifications using Cron Jobs

We also send notifications automatically when a technician works longer than 2 hours.

Cron Job Example

cron.schedule('*/5 * * * * *', async () => {
  const inProgressTechServices = await TechnicianUserService.find({
    status: "in-progress",
    adminNotified: { $ne: true }
  });

  for (const techService of inProgressTechServices) {
    let totalSeconds = techService.workDuration || 0;

    if (techService.workStartedAt) {
      totalSeconds += Math.floor((Date.now() - techService.workStartedAt) / 1000);
    }

    if (totalSeconds >= 7200) {
      await Notification.create({
        title: "Technician work exceeded 2 hours",
        message: "Service request exceeded time limit",
        type: "work_overdue"
      });

      techService.adminNotified = true;
      await techService.save();
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

This ensures:

  • Notifications are automatic
  • No duplicate alerts
  • Works even if no user is logged in

⚛️ Frontend (React) – Listening to Notifications

On the React side:

import { io } from "socket.io-client";

const socket = io("http://localhost:5000");

useEffect(() => {
  socket.on("notification", (data) => {
    console.log("New Notification:", data);
  });

  return () => socket.off("notification");
}, []);
Enter fullscreen mode Exit fullscreen mode

🧠 Best Practices

✅ Store notifications in DB
✅ Emit only IDs or small payloads
✅ Use Socket.IO rooms for scalability
✅ Combine REST APIs + sockets
❌ Don’t rely on sockets alone for persistence

🎯 Final Thoughts

Socket.IO works best when:

  • REST handles data
  • Sockets handle events

Top comments (0)