🔌 Real-time Communication — Pick the Right Tool
WebSocket 🔁
Full-duplex, persistent TCP connection. Both sides can send anytime.
✅ Use when: Chat, multiplayer games, live trading, collaborative editing, live sports scores
❌ Avoid when: Client only reads data (no need for bidirectional), simple notifications
// Client
const ws = new WebSocket("wss://api.example.com/ws");
ws.onopen = () => ws.send(JSON.stringify({ type:"join" }));
ws.onmessage = (e) => console.log(JSON.parse(e.data));
ws.onerror = (e) => console.error(e);
ws.onclose = () => console.log("disconnected");
// Need reconnect logic + heartbeat in production
Server-Sent Events (SSE) 📡
One-way stream: server → client only. Runs over plain HTTP/1.1. Auto-reconnects natively.
✅ Use when: Live feed, notifications, dashboards, progress updates
⚠ Limit: Client-to-server still needs regular HTTP requests
// Server sends text/event-stream
// Client
const es = new EventSource("/api/stream");
es.onmessage = (e) => console.log(e.data);
es.onerror = () => es.close();
// Named events
es.addEventListener("update", (e) => handle(e.data));
// Auto-reconnects with Last-Event-ID header 🎉
Long Polling 🔄
Client sends request → server holds until data is ready → responds → client immediately re-requests.
⚠ Use when: Legacy browsers, no WebSocket support, infrequent updates
❌ Avoid for: High-frequency data — creates thundering herd, high server load
async function longPoll() {
while (true) {
try {
const res = await fetch("/api/poll?since=" + lastId);
const data = await res.json();
process(data);
lastId = data.lastId;
} catch { await sleep(1000); }
}
}
Short Polling ⏱️
Client sends HTTP request every N seconds regardless of whether data changed.
⚠ Use when: Non-critical updates, very simple implementation needed
❌ Wasteful — hits server even when no new data
// Simple but inefficient
setInterval(async () => {
const data = await fetch("/api/status").then(r => r.json());
render(data);
}, 5000);
Quick Comparison Table ⚡
| Protocol | Direction | Latency | Auto-reconnect | Use case |
|---|---|---|---|---|
| WebSocket | Bidirectional | Very low | Manual | Chat, gaming, collab |
| SSE | Server→Client | Low | Native ✅ | Notifications, feeds |
| Long Polling | Server→Client | Medium | Manual | Legacy support |
| Short Polling | Server→Client | High | N/A (always on) | Simple status checks |
| HTTP/2 Push | Server→Client | Low | Manual | Proactive resource push |
| gRPC Streaming | Bidirectional | Very low | Manual | Microservices, internal APIs |
🌐 HTTP Protocols
HTTP/1.1 vs HTTP/2 vs HTTP/3
HTTP/1.1 1 request per TCP connection (with keep-alive: 6 parallel). HOL blocking.
HTTP/2 Multiplexing — many requests on 1 TCP. Header compression. Server push. Streams.
HTTP/3 QUIC (UDP-based). Faster handshake. No TCP HOL blocking. Better on lossy networks.
HTTP/2 is default for modern HTTPS sites. Fixes domain sharding anti-pattern from HTTP/1.
REST vs GraphQL vs gRPC
REST Resource-based URLs. Easy to cache. Over/under-fetching problem.
GraphQL Query exactly what you need. Single endpoint. Great for complex UIs. Harder to cache.
gRPC Binary (protobuf). Streaming. Typed. Best for internal microservices.
tRPC Type-safe APIs between TS client + server. No codegen.
Interview tip: Choose REST for simple CRUD, GraphQL when clients need flexible data shapes, gRPC for performance-critical internal services.
HTTP Caching
Cache-Control: no-store Never cache
Cache-Control: no-cache Cache but always revalidate
Cache-Control: max-age=3600 Cache for 1 hour
Cache-Control: immutable Never revalidate (use content hash in URL)
ETag Server sends hash; client sends If-None-Match; server returns 304 if unchanged
Last-Modified Same idea with timestamps
CDN & Edge
CDN caches static assets close to users. Edge functions run at CDN POPs.
- ✓ Static assets: aggressive cache (1yr) with content hash in filename
- ✓ HTML: short cache or no-cache (needs to stay fresh)
- ✓ API: CDN caching for public data, bypass for private
- ✓ Image CDN: resize/optimize on-the-fly (Cloudinary, Imgix, Next/Image)