Topic 8 of 55
WebSockets & Real-Time
Overview
WebSockets provide a persistent, bidirectional connection between client and server — enabling real-time features like live chat, notifications, collaborative editing, and live dashboards that are impossible with traditional HTTP request-response.
Syntax
javascript
// Server: Socket.io
import { Server } from 'socket.io';
import { createServer } from 'http';
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: { origin: 'http://localhost:3000', methods: ['GET', 'POST'] }
});
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// Join a room
socket.on('join-room', (roomId) => {
socket.join(roomId);
});
// Handle message
socket.on('send-message', (data) => {
io.to(data.roomId).emit('new-message', data);
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
// Client: React
import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';
function ChatRoom({ roomId }) {
const socketRef = useRef<Socket>();
useEffect(() => {
socketRef.current = io('http://localhost:3001');
socketRef.current.emit('join-room', roomId);
socketRef.current.on('new-message', handleMessage);
return () => { socketRef.current?.disconnect(); }; // cleanup!
}, [roomId]);
}Common Pitfalls
- Socket.io uses long-polling as a fallback when WebSockets are unavailable — configure transports: ['websocket'] to force WS only.
- In a multi-server setup, Socket.io rooms don't work across servers — use Redis adapter (socket.io-adapter-redis) for horizontal scaling.
- Interview tip: WebSockets are stateful — you need sticky sessions or a shared pub/sub system (Redis, Kafka) to scale horizontally.
Real-World Example
Live order tracking with Socket.io rooms:
example
javascript
// server — real-time order updates
import { Server } from 'socket.io';
const io = new Server(httpServer);
// Middleware for auth on socket connection
io.use(async (socket, next) => {
const token = socket.handshake.auth.token;
try {
const user = await verifyToken(token);
socket.data.user = user;
next();
} catch {
next(new Error('Authentication error'));
}
});
io.on('connection', (socket) => {
const userId = socket.data.user.id;
// Each user gets their own room
socket.join(`user:${userId}`);
console.log(`User ${userId} connected for order tracking`);
});
// Call from your order service when status changes
export function notifyOrderUpdate(userId: string, order: Order) {
io.to(`user:${userId}`).emit('order:updated', {
orderId: order.id,
status: order.status,
estimatedDelivery: order.estimatedDelivery,
trackingUrl: order.trackingUrl,
});
}
// Usage in order processing
async function updateOrderStatus(orderId: string, status: string) {
const order = await db.orders.update({ where: { id: orderId }, data: { status } });
notifyOrderUpdate(order.userId, order); // push to user's browser!
return order;
}