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);
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);
Socket.IO runs alongside your REST APIs—not instead of them.
🔔 Emitting Notifications on User Signup
When a user completes signup, we:
- Save the notification in MongoDB
- 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);
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();
}
}
});
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");
}, []);
🧠 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)