Node.js
WebSockets
Real-time bidirectional communication with Socket.io — server setup, client connection, event emitting, rooms, namespaces, broadcasts, and disconnect handling.
Socket.io Install & Server Setup: Install Socket.io and attach it to an existing HTTP server. Configure CORS for browser clients.
server.js
JS
// npm install socket.io
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: 'http://localhost:5173', // frontend URL
methods: ['GET', 'POST'],
},
pingTimeout: 60000,
pingInterval: 25000,
});
// Make io accessible from controllers
app.set('io', io);
server.listen(3000, () => console.log('Socket.io server on port 3000'));
module.exports = { io };
Client Connection & Basic Events: Listen for client connections on the server and handle the fundamental connection lifecycle.
sockets/index.js
JS
const registerSocketHandlers = (io) => {
io.on('connection', (socket) => {
console.log(`Client connected: ${socket.id}`);
// Receive event from client
socket.on('message', (data) => {
console.log('Received:', data);
});
// Send event back to this client only
socket.emit('welcome', { message: `Hello ${socket.id}` });
// Handle disconnect
socket.on('disconnect', (reason) => {
console.log(`Client ${socket.id} disconnected: ${reason}`);
});
});
};
module.exports = registerSocketHandlers;
// In server.js:
// const registerSocketHandlers = require('./sockets/index');
// registerSocketHandlers(io);
Emitting Events: Different emit targets — send to one client, all clients, or everyone except the sender.
sockets/index.js – emit patterns
JS
io.on('connection', (socket) => {
// To this socket only
socket.emit('event', { data: 'private' });
// To all connected clients (including sender)
io.emit('broadcast', { data: 'to everyone' });
// To all clients EXCEPT the sender
socket.broadcast.emit('news', { data: 'from someone else' });
// Send acknowledgment (callback from client)
socket.emit('ping', { time: Date.now() }, (ack) => {
console.log('Client acknowledged:', ack);
});
// Emit to a specific socket ID
const targetId = 'abc123';
io.to(targetId).emit('private', { message: 'direct message' });
});
Rooms: Group sockets into named rooms for targeted broadcasts — perfect for chat rooms, game lobbies, or per-user channels.
sockets/chatHandler.js
JS
io.on('connection', (socket) => {
// Join a room
socket.on('joinRoom', (roomId) => {
socket.join(roomId);
console.log(`${socket.id} joined room: ${roomId}`);
// Notify others in the room
socket.to(roomId).emit('userJoined', { id: socket.id });
// Emit only to members of this room (including sender)
io.to(roomId).emit('roomInfo', { members: io.sockets.adapter.rooms.get(roomId)?.size });
});
// Leave a room
socket.on('leaveRoom', (roomId) => {
socket.leave(roomId);
socket.to(roomId).emit('userLeft', { id: socket.id });
});
// Send message to room
socket.on('chatMessage', ({ roomId, message }) => {
io.to(roomId).emit('newMessage', {
from: socket.id,
message,
at: new Date().toISOString(),
});
});
});
Namespaces: Create separate communication channels on the same Socket.io server — useful for separating admin, user, and support socket spaces.
sockets/namespaces.js
JS
// Admin namespace
const adminNsp = io.of('/admin');
adminNsp.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!isValidAdminToken(token)) return next(new Error('Unauthorized'));
next();
});
adminNsp.on('connection', (socket) => {
console.log('Admin connected:', socket.id);
socket.emit('adminWelcome', { msg: 'Admin panel ready' });
});
// Chat namespace
const chatNsp = io.of('/chat');
chatNsp.on('connection', (socket) => {
socket.on('send', (msg) => {
chatNsp.emit('receive', msg); // broadcast within /chat namespace
});
});
// Client connection (frontend)
// const adminSocket = io('http://localhost:3000/admin', { auth: { token: '...' } });
// const chatSocket = io('http://localhost:3000/chat');
Disconnect & Cleanup Handling: Properly clean up user sessions, notify peers, and remove stale data when a socket disconnects.
sockets/presenceHandler.js
JS
const onlineUsers = new Map(); // socketId -> userId
io.on('connection', (socket) => {
// Register user
socket.on('register', (userId) => {
onlineUsers.set(socket.id, userId);
io.emit('onlineUsers', [...onlineUsers.values()]);
console.log(`User ${userId} is online`);
});
// Disconnecting fires before rooms are left
socket.on('disconnecting', () => {
socket.rooms.forEach((room) => {
if (room !== socket.id) {
socket.to(room).emit('userLeft', { socketId: socket.id });
}
});
});
// Disconnect fires after cleanup
socket.on('disconnect', (reason) => {
const userId = onlineUsers.get(socket.id);
onlineUsers.delete(socket.id);
io.emit('onlineUsers', [...onlineUsers.values()]);
console.log(`User ${userId} disconnected. Reason: ${reason}`);
});
});