WebSockets & Real-Time
Live Updates Without Refreshing
Open interactive version (quiz + challenge)Real-world analogy
Normal HTTP is like sending letters — you send a question, wait for a reply. WebSockets are like a phone call — once connected, both sides can talk anytime! That's how live chat, notifications, and collaborative editing work.
What is it?
WebSockets provide a persistent, bidirectional communication channel between client and server. Unlike HTTP (request-response), WebSocket connections stay open, allowing the server to push data to clients in real-time.
Real-world relevance
Slack, Discord, WhatsApp Web, Google Docs collaborative editing, live sports scores, stock tickers — all use WebSockets for real-time updates.
Key points
- Persistent Connection — Unlike HTTP where each request opens a new connection (connect, send, disconnect, repeat), WebSockets establish a single persistent connection that stays open. Both client and server can send messages at any time without the overhead of reconnecting. This enables true real-time two-way communication with minimal latency.
- Socket.IO — Socket.IO is the most popular WebSocket library for JavaScript. It wraps raw WebSockets with powerful features: automatic reconnection when the connection drops, room-based messaging for group communication, binary data support, and graceful fallbacks for environments where WebSockets are not available.
- Rooms — Rooms let you group WebSocket connections together so you can broadcast messages to specific subsets of users. For example, everyone in Chat Room 5 receives new messages but users in other rooms do not. Rooms are also useful for game lobbies, collaborative document sessions, and per-user notification channels.
- NestJS Gateway — NestJS provides built-in WebSocket support through the @WebSocketGateway() decorator. Define event handlers with @SubscribeMessage('eventName') just like you define HTTP routes with @Get(). The gateway integrates with NestJS dependency injection, guards, and interceptors for clean, organized real-time code.
- Broadcasting — Send messages to a single connected client, a specific room (group of users), or broadcast to every connected user at once. Socket.IO provides flexible methods: socket.emit (one user), socket.to('room').emit (room only), and io.emit (everyone). This gives you precise control over who receives each update.
- Reconnection Logic — Network interruptions are inevitable on mobile devices and unstable connections. Socket.IO handles reconnection automatically with configurable retry intervals and exponential backoff. The client detects disconnection, queues outgoing messages, reconnects seamlessly, and replays missed messages — users barely notice the blip.
- Namespaces for Separation — Socket.IO namespaces let you separate WebSocket logic into distinct channels on the same server. Create /chat for messaging, /notifications for alerts, and /live-scores for sports updates. Each namespace has its own event handlers, rooms, and middleware — keeping your real-time code organized and maintainable.
- Authentication for WebSockets — WebSocket connections need authentication just like HTTP routes. Pass the JWT token during the connection handshake and verify it in a middleware or guard before allowing the socket to join. Without authentication, anyone could connect and listen to private messages. NestJS guards work seamlessly with WebSocket gateways.
Code example
// 1. NestJS WebSocket Gateway 🚀
@WebSocketGateway({
cors: { origin: '*' },
})
export class ChatGateway {
@WebSocketServer()
server: Server;
// When a client sends a message
@SubscribeMessage('sendMessage')
handleMessage(client: Socket, payload: { room: string; text: string }) {
// Broadcast to everyone in the room
this.server.to(payload.room).emit('newMessage', {
text: payload.text,
sender: client.id,
timestamp: new Date(),
});
}
// When a client joins a room
@SubscribeMessage('joinRoom')
handleJoinRoom(client: Socket, room: string) {
client.join(room);
this.server.to(room).emit('userJoined', {
message: `Someone joined the room!`,
});
}
}
// 2. React client — connect and listen 📡
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000');
function ChatRoom({ room }: { room: string }) {
const [messages, setMessages] = useState<any[]>([]);
useEffect(() => {
socket.emit('joinRoom', room);
socket.on('newMessage', (msg) => {
setMessages(prev => [...prev, msg]);
});
return () => { socket.off('newMessage'); };
}, [room]);
const sendMessage = (text: string) => {
socket.emit('sendMessage', { room, text });
};
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg.text}</div>
))}
<input placeholder="Type..." onKeyDown={(e) => {
if (e.key === 'Enter') sendMessage(e.currentTarget.value);
}} />
</div>
);
}Line-by-line walkthrough
- 1. 1. NestJS WebSocket Gateway 🚀
- 2. Decorator that adds metadata or behavior
- 3.
- 4.
- 5. Exporting for use in other files
- 6. Decorator that adds metadata or behavior
- 7.
- 8.
- 9. When a client sends a message
- 10. Decorator that adds metadata or behavior
- 11.
- 12. Broadcast to everyone in the room
- 13.
- 14.
- 15.
- 16.
- 17.
- 18. Closing block
- 19.
- 20. When a client joins a room
- 21. Decorator that adds metadata or behavior
- 22.
- 23.
- 24.
- 25.
- 26.
- 27. Closing block
- 28. Closing block
- 29.
- 30. 2. React client — connect and listen 📡
- 31. Importing required dependencies
- 32.
- 33. Declaring a variable
- 34.
- 35. Declaring a function
- 36. Declaring a variable
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45. Returning a value
- 46.
- 47.
- 48. Declaring a variable
- 49.
- 50. Closing block
- 51.
- 52. Returning a value
- 53.
- 54.
- 55.
- 56.
- 57.
- 58. Conditional check
- 59.
- 60.
- 61. Closing expression
- 62. Closing block
Spot the bug
@SubscribeMessage('sendMessage')
handleMessage(client: Socket, payload: { room: string; text: string }) {
client.emit('newMessage', { text: payload.text });
}Need a hint?
Who receives the message when you emit on client?
Show answer
client.emit() sends only to the sender, not everyone in the room. Fix: use this.server.to(payload.room).emit('newMessage', ...) to broadcast to the room.
Explain like I'm 5
Regular websites are like sending letters - you send a question, wait for an answer. WebSockets are like a phone call - once connected, both people can talk anytime! That's how live chat works - messages show up right away without refreshing.
Fun fact
Discord handles over 4 million WebSocket connections simultaneously on a single server using Elixir. That's like 4 million people on a phone call at the same time on one phone line! 📞
Hands-on challenge
Build a collaborative 'cursor tracker' — when a user moves their mouse on the page, broadcast their cursor position and username to all other connected users. Display colored dots for each user's cursor in real-time. Add a 'user is typing...' indicator that disappears after 2 seconds of inactivity. How would you handle 50+ users without flooding the network?
More resources
- NestJS WebSockets (NestJS Official)
- Socket.io Documentation (Socket.io Official)
- WebSockets in 100 Seconds (Fireship)