Episode 3 — NodeJS MongoDB Backend Architecture / 3.15 — Realtime Communication WebSockets
3.15.a — Understanding WebSockets
WebSocket is a communication protocol providing full-duplex, persistent connections over a single TCP connection, enabling real-time data exchange between client and server without repeated HTTP requests.
<< README | Next: 3.15.b — HTTP Polling & Alternatives >>
1. What is the WebSocket Protocol?
WebSocket is a communication protocol defined in RFC 6455 that provides a persistent, full-duplex communication channel over a single TCP connection. Unlike HTTP, where the client always initiates communication, WebSocket allows both client and server to send messages independently at any time.
Traditional HTTP:
Client ──Request──> Server
Client <──Response── Server
(Connection closes)
WebSocket:
Client <══════════════> Server
(Persistent bidirectional connection)
Either side can send data at any time
Think of HTTP as a walkie-talkie (one speaks, the other listens, then responds) and WebSocket as a phone call (both parties can speak and listen simultaneously).
2. HTTP vs WebSocket: The Fundamental Difference
| Feature | HTTP | WebSocket |
|---|---|---|
| Communication | Request-Response (half-duplex) | Full-duplex (bidirectional) |
| Connection | New connection per request (HTTP/1.1 uses keep-alive) | Single persistent connection |
| Who initiates? | Client always initiates | Either side can send anytime |
| Overhead | Headers sent with every request (~800 bytes+) | Minimal frame overhead (~2-6 bytes) |
| Protocol | http:// / https:// | ws:// / wss:// |
| State | Stateless by default | Stateful — connection stays open |
| Use case | CRUD operations, page loads, APIs | Real-time updates, chat, gaming |
| Server Push | Not native (workarounds exist) | Native — server pushes freely |
HTTP Timeline:
t=0s Client: GET /messages → Server responds with messages
t=5s Client: GET /messages → Server responds (same or new)
t=10s Client: GET /messages → Server responds (same or new)
↑ Client must keep asking. Wasteful if nothing changed.
WebSocket Timeline:
t=0s Client connects → Connection established
t=3s Server: "New message from Alice" → Client receives instantly
t=7s Client: "Reply to Alice" → Server receives instantly
t=12s Server: "Alice is typing..." → Client receives instantly
↑ Data flows freely in both directions. No wasted requests.
3. The WebSocket Handshake: HTTP Upgrade Mechanism
WebSocket connections begin with a standard HTTP request that gets upgraded to a WebSocket connection. This is called the WebSocket handshake.
Step-by-Step Handshake
Step 1: Client sends HTTP GET with Upgrade headers
─────────────────────────────────────────────────
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Step 2: Server responds with 101 Switching Protocols
─────────────────────────────────────────────────────
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Step 3: Connection is now WebSocket (no more HTTP)
──────────────────────────────────────────────────
Both sides can freely send WebSocket frames.
Key headers explained:
| Header | Purpose |
|---|---|
Upgrade: websocket | Requests protocol switch to WebSocket |
Connection: Upgrade | Signals the connection should be upgraded |
Sec-WebSocket-Key | Random base64-encoded value for security handshake |
Sec-WebSocket-Accept | Server's proof it received the key (SHA-1 hash) |
Sec-WebSocket-Version | Protocol version (13 is current standard) |
Because the handshake starts as HTTP, WebSocket connections work through standard web infrastructure (proxies, load balancers, firewalls) that understand HTTP.
4. WebSocket Protocols: ws:// and wss://
ws://example.com/socket → Unencrypted WebSocket (like HTTP)
wss://example.com/socket → Encrypted WebSocket (like HTTPS, uses TLS)
| Protocol | Encryption | Default Port | Use Case |
|---|---|---|---|
ws:// | None | 80 | Local development only |
wss:// | TLS/SSL | 443 | Production — ALWAYS use this |
Always use wss:// in production. Unencrypted WebSocket connections are vulnerable to man-in-the-middle attacks, and many corporate proxies/firewalls will block ws:// connections.
// Client connection examples
const devSocket = new WebSocket('ws://localhost:3000');
const prodSocket = new WebSocket('wss://api.myapp.com/socket');
5. WebSocket Lifecycle
Every WebSocket connection follows a predictable lifecycle with three main phases:
┌──────────┐ ┌────────────────────┐ ┌──────────┐
│ OPEN │───>│ MESSAGE EXCHANGE │───>│ CLOSE │
│ (once) │ │ (many times) │ │ (once) │
└──────────┘ └────────────────────┘ └──────────┘
Native Browser WebSocket API
// Creating a WebSocket connection
const socket = new WebSocket('wss://api.example.com/ws');
// 1. OPEN — Connection established
socket.addEventListener('open', (event) => {
console.log('Connected to server!');
socket.send('Hello, Server!');
});
// 2. MESSAGE — Data received from server
socket.addEventListener('message', (event) => {
console.log('Server says:', event.data);
// Parse JSON if server sends JSON
const data = JSON.parse(event.data);
console.log('Parsed:', data);
});
// 3. CLOSE — Connection closed
socket.addEventListener('close', (event) => {
console.log('Disconnected:', event.code, event.reason);
// event.code: 1000 = normal, 1006 = abnormal, etc.
// event.wasClean: boolean — was it a clean close?
});
// 4. ERROR — Something went wrong
socket.addEventListener('error', (event) => {
console.error('WebSocket error:', event);
});
// Sending data to the server
socket.send('Hello!'); // String
socket.send(JSON.stringify({ type: 'chat', message: 'Hi' })); // JSON
// Closing the connection
socket.close(1000, 'Normal closure'); // code, reason
Close Codes Reference
| Code | Meaning |
|---|---|
| 1000 | Normal closure |
| 1001 | Going away (page navigation, server shutdown) |
| 1002 | Protocol error |
| 1003 | Unsupported data type |
| 1006 | Abnormal closure (no close frame received) |
| 1008 | Policy violation |
| 1011 | Server encountered unexpected condition |
6. Use Cases for WebSocket
WebSocket excels in scenarios requiring instant, bidirectional communication:
Chat Applications
// Server receives message and broadcasts to all users in room
socket.on('chat-message', (data) => {
io.to(data.room).emit('new-message', {
user: socket.user.name,
text: data.text,
timestamp: Date.now()
});
});
Live Notifications
// Server pushes notification when something happens
function notifyUser(userId, notification) {
io.to(`user:${userId}`).emit('notification', {
title: notification.title,
body: notification.body,
link: notification.link,
createdAt: new Date()
});
}
Collaborative Editing (Google Docs style)
// User makes a change → broadcast to all collaborators
socket.on('document-change', (delta) => {
socket.to(`doc:${delta.documentId}`).emit('remote-change', {
userId: socket.user.id,
operations: delta.ops,
version: delta.version
});
});
Live Sports / Stock Tickers
// Server pushes price updates as they happen
function broadcastPriceUpdate(symbol, price) {
io.to(`stock:${symbol}`).emit('price-update', {
symbol,
price,
change: calculateChange(symbol, price),
timestamp: Date.now()
});
}
Multiplayer Gaming
// Player position updates at high frequency
socket.on('player-move', (position) => {
socket.to(gameRoom).emit('player-moved', {
playerId: socket.id,
x: position.x,
y: position.y,
velocity: position.velocity
});
});
7. Comparison: Polling vs Long Polling vs SSE vs WebSocket
| Feature | Short Polling | Long Polling | SSE | WebSocket |
|---|---|---|---|---|
| Direction | Client → Server | Client → Server | Server → Client | Bidirectional |
| Connection | New per request | Held open, then new | Persistent (one-way) | Persistent (two-way) |
| Latency | High (interval-based) | Medium | Low | Very Low |
| Server load | High (many requests) | Medium | Low | Low |
| Complexity | Simple | Medium | Simple | Medium-High |
| Browser support | Universal | Universal | Modern browsers | Modern browsers |
| HTTP compatible | Yes | Yes | Yes | Starts as HTTP, then upgrades |
| Binary data | Via encoding | Via encoding | No (text only) | Yes (native) |
| Auto-reconnect | N/A (new requests) | Must implement | Built-in | Must implement |
| Best for | Simple dashboards | Moderate updates | News feeds, logs | Chat, gaming, collab |
8. When NOT to Use WebSocket
WebSocket is not always the right choice:
| Scenario | Better Alternative | Why |
|---|---|---|
| Simple CRUD operations | REST API (HTTP) | No need for persistent connections |
| Infrequent updates (every 30+ seconds) | Short polling or SSE | WebSocket overhead not justified |
| One-way server updates only | SSE | Simpler, automatic reconnection |
| Static content delivery | HTTP + CDN | WebSocket adds unnecessary complexity |
| SEO-critical content | HTTP | Search engines understand HTTP, not WS |
| Simple form submissions | HTTP POST | Standard HTTP is perfectly fine |
Rule of thumb: If you only need to send data from server to client, consider SSE first. If communication is infrequent, consider polling. Use WebSocket when you need frequent, bidirectional, low-latency communication.
Key Takeaways
- WebSocket provides full-duplex communication over a single persistent TCP connection
- The connection starts with an HTTP Upgrade handshake (101 Switching Protocols)
- Always use
wss://(encrypted) in production environments - The lifecycle is simple: Open -> Message (many) -> Close
- WebSocket excels at chat, notifications, collaborative editing, gaming, and live data
- It is not a replacement for HTTP -- use it only when real-time bidirectional communication is needed
- Overhead per message is tiny (~2-6 bytes) compared to HTTP headers (~800+ bytes)
Explain-It Challenge
Scenario: Your team is building a project management tool (like Trello). The PM asks: "Should we use WebSocket for the entire application?"
Explain which parts of the application would benefit from WebSocket (and why) and which parts should remain as standard REST API calls. Consider features like: viewing boards, creating tasks, dragging cards between columns, real-time collaboration when multiple users view the same board, and user profile settings. Justify each decision.