WebSocket Chat with Socket.io
Rooms, namespaces, auth middleware, reconnection, and scaling
What You’ll Build
After following this guide, you will have a working implementation of websocket chat with socket.io in your project. Build a real-time chat application with Socket.io. Covers rooms (separate chat channels), authentication middleware (verify JWT before connection), typing indicators, online presence, and graceful reconnection. Socket.io handles WebSocket fallbacks and auto-reconnection.
Use Cases & Problems Solved
- Build reliable server endpoints that clients can consume consistently
- Handle common backend patterns like routing, middleware, and error handling
- Provide a clean interface between your frontend and data layer
Prerequisites
- Node.js 18+
- Express.js
Step-by-Step Implementation
Install Socket.io
The following snippet shows how to install socket.io. Copy this into your project and adjust the values for your environment.
npm install socket.io socket.io-client
Server setup with auth and rooms
The following snippet shows how to server setup with auth and rooms. Copy this into your project and adjust the values for your environment.
const { Server } = require('socket.io');
const jwt = require('jsonwebtoken');
const io = new Server(httpServer, { cors: { origin: '*' } });
// Auth middleware — verify JWT before allowing connection
io.use((socket, next) => {
const token = socket.handshake.auth.token;
try {
socket.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (err) {
next(new Error('Authentication failed'));
}
});
io.on('connection', (socket) => {
console.log(\`\${socket.user.name} connected\`);
socket.on('join-room', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user-joined', { user: socket.user.name });
});
socket.on('message', ({ roomId, text }) => {
io.to(roomId).emit('message', {
user: socket.user.name,
text,
timestamp: new Date().toISOString(),
});
});
socket.on('typing', ({ roomId }) => {
socket.to(roomId).emit('typing', { user: socket.user.name });
});
socket.on('disconnect', () => {
console.log(\`\${socket.user.name} disconnected\`);
});
});
Client connection with auto-reconnect
The following snippet shows how to client connection with auto-reconnect. Copy this into your project and adjust the values for your environment.
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', {
auth: { token: localStorage.getItem('jwt') },
reconnection: true,
reconnectionAttempts: 10,
reconnectionDelay: 1000,
});
socket.emit('join-room', 'general');
socket.on('message', (msg) => appendMessage(msg));
socket.on('typing', ({ user }) => showTypingIndicator(user));
// Send message
function sendMessage(text) {
socket.emit('message', { roomId: 'general', text });
}
⚠️ Don’t Do This
❌ Broadcasting to ALL connected users instead of a specific room
// Every user gets every message from every room!
io.emit('message', { room: roomId, text }); // BAD: io.emit is global!
✅ Use io.to(room) to send messages only to room members
// Only users in 'room-123' receive this message
io.to('room-123').emit('message', { text });
// To everyone in room EXCEPT sender:
socket.to('room-123').emit('message', { text });
Testing
Add these tests to verify your API endpoints work correctly:
// __tests__/api.test.ts
import { describe, it, expect } from 'vitest';
describe('WebSocket Chat with Socket.io', () => {
it('should return 200 for valid requests', async () => {
const res = await fetch('/api/endpoint', { method: 'GET' });
expect(res.status).toBe(200);
const data = await res.json();
expect(data).toBeDefined();
});
it('should return 400 for invalid input', async () => {
const res = await fetch('/api/endpoint', {
method: 'POST',
body: JSON.stringify({}),
});
expect(res.status).toBe(400);
});
it('should handle errors gracefully', async () => {
const res = await fetch('/api/endpoint/nonexistent');
expect(res.status).toBe(404);
});
});
Verification
# Open two browser tabs to your chat app
# Join the same room in both tabs
# Send a message in one tab — should appear in both
# Type in one tab — typing indicator should appear in the other Related Specs
Real-Time Updates with SSE
EventSource API, reconnection, broadcasting, and Express/Fastify SSE
Production-Ready REST API (Express)
Error handling, Zod validation, rate limiting, CORS, and structured logging
Background Jobs with BullMQ
Queue setup, workers, retries, job scheduling, and monitoring dashboard