Socket.IO Integration for Real-Time Website Communication
Socket.IO is a library for bidirectional server-client communication. Unlike raw WebSocket, Socket.IO provides automatic reconnection, long-polling fallback, rooms, and namespaces.
Installation and Basic Setup
npm install socket.io # server
npm install socket.io-client # client
Server (Node.js + Express)
import { createServer } from 'http';
import { Server } from 'socket.io';
import express from 'express';
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: process.env.CLIENT_URL,
credentials: true
},
pingTimeout: 60000,
pingInterval: 25000
});
// Authentication middleware
io.use(async (socket, next) => {
const token = socket.handshake.auth.token;
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
socket.data.userId = payload.sub;
socket.data.user = await userRepo.findById(payload.sub);
next();
} catch {
next(new Error('Authentication error'));
}
});
io.on('connection', (socket) => {
const userId = socket.data.userId;
console.log(`User ${userId} connected: ${socket.id}`);
// Join personal room
socket.join(`user:${userId}`);
// Event handlers
socket.on('join:room', async ({ roomId }) => {
const canJoin = await checkRoomAccess(userId, roomId);
if (!canJoin) {
socket.emit('error', { message: 'Access denied' });
return;
}
socket.join(`room:${roomId}`);
socket.to(`room:${roomId}`).emit('user:joined', {
userId, user: socket.data.user
});
});
socket.on('message:send', async ({ roomId, content }) => {
const message = await messageRepo.create({ roomId, userId, content });
io.to(`room:${roomId}`).emit('message:new', message);
});
socket.on('disconnect', () => {
console.log(`User ${userId} disconnected`);
// Notify rooms of exit
io.emit('user:offline', { userId });
});
});
httpServer.listen(3000);
Client (React)
// hooks/useSocket.ts
import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';
import { useAuthStore } from '@/stores/auth';
export function useSocket(): Socket | null {
const socketRef = useRef<Socket | null>(null);
const token = useAuthStore(s => s.token);
useEffect(() => {
if (!token) return;
const socket = io(process.env.NEXT_PUBLIC_WS_URL, {
auth: { token },
reconnection: true,
reconnectionDelay: 1000,
reconnectionAttempts: 5,
transports: ['websocket', 'polling']
});
socket.on('connect', () => console.log('Socket connected'));
socket.on('connect_error', (err) => console.error('Connection error:', err));
socketRef.current = socket;
return () => {
socket.disconnect();
socketRef.current = null;
};
}, [token]);
return socketRef.current;
}
// Chat component
function ChatRoom({ roomId }) {
const socket = useSocket();
const [messages, setMessages] = useState([]);
useEffect(() => {
if (!socket) return;
socket.emit('join:room', { roomId });
socket.on('message:new', (message) => {
setMessages(prev => [...prev, message]);
});
return () => {
socket.off('message:new');
socket.emit('leave:room', { roomId });
};
}, [socket, roomId]);
const sendMessage = (content: string) => {
socket?.emit('message:send', { roomId, content });
};
return <ChatUI messages={messages} onSend={sendMessage} />;
}
Scaling via Redis Adapter
With multiple Node.js processes Socket.IO needs Redis for sync:
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
// Now io.to('room:123').emit() works across all Node.js processes
Notifications from Other Services
// From backend service send notification to specific user
// via Redis pub/sub (no direct Socket.IO server access)
const publisher = createClient({ url: process.env.REDIS_URL });
await publisher.publish('socket:notify', JSON.stringify({
room: `user:${userId}`,
event: 'order:status_changed',
data: { orderId, newStatus: 'shipped' }
}));
Timeline
Socket.IO server + client hook + chat rooms + Redis adapter: 1–2 weeks.







