Real-time features transform static applications into dynamic experiences. Live notifications, collaborative editing, real-time dashboards—users expect instant updates. AI can help you implement these features correctly from the start.
Choosing the Right Technology#
WebSockets#
Full-duplex communication for bidirectional, low-latency needs:
1// Client
2const ws = new WebSocket('wss://api.example.com/ws');
3
4ws.onmessage = (event) => {
5 const data = JSON.parse(event.data);
6 handleUpdate(data);
7};
8
9ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));Best for: Chat, gaming, collaborative editing, trading platforms
Server-Sent Events (SSE)#
One-way server-to-client streaming:
1// Client
2const eventSource = new EventSource('/api/events');
3
4eventSource.onmessage = (event) => {
5 const data = JSON.parse(event.data);
6 handleUpdate(data);
7};
8
9// Server
10app.get('/api/events', (req, res) => {
11 res.setHeader('Content-Type', 'text/event-stream');
12 res.setHeader('Cache-Control', 'no-cache');
13
14 const interval = setInterval(() => {
15 res.write(`data: ${JSON.stringify(getUpdates())}\n\n`);
16 }, 1000);
17
18 req.on('close', () => clearInterval(interval));
19});Best for: Notifications, feeds, dashboards, progress updates
Long Polling#
Fallback for restricted environments:
1async function poll() {
2 try {
3 const response = await fetch('/api/updates?timeout=30');
4 const data = await response.json();
5 handleUpdate(data);
6 } finally {
7 poll(); // Immediately reconnect
8 }
9}Best for: When WebSockets and SSE aren't available
Pattern 1: Real-Time Notifications#
Design a notification system with these requirements:
Features:
- Push notifications to online users
- Persist unread notifications for offline users
- Support notification types: mention, like, follow, system
- Allow muting/preferences per type
Tech stack:
- Node.js backend
- Redis for pub/sub
- PostgreSQL for persistence
- React frontend
Include:
- WebSocket server implementation
- Notification service
- Frontend hook for consuming notifications
Pattern 2: Live Collaboration#
Implement collaborative text editing:
Requirements:
- Multiple users edit same document
- Real-time cursor positions
- Conflict resolution for simultaneous edits
- Offline support with sync
Approaches to evaluate:
- Operational Transformation (OT)
- Conflict-free Replicated Data Types (CRDTs)
Provide architecture and key implementation details.
Pattern 3: Real-Time Dashboard#
1// React hook for real-time metrics
2function useRealtimeMetrics(metricIds: string[]) {
3 const [metrics, setMetrics] = useState<Record<string, MetricValue>>({});
4
5 useEffect(() => {
6 const ws = new WebSocket(`wss://api.example.com/metrics`);
7
8 ws.onopen = () => {
9 ws.send(JSON.stringify({
10 type: 'subscribe',
11 metrics: metricIds
12 }));
13 };
14
15 ws.onmessage = (event) => {
16 const update = JSON.parse(event.data);
17 setMetrics(prev => ({
18 ...prev,
19 [update.metricId]: update.value
20 }));
21 };
22
23 return () => ws.close();
24 }, [metricIds.join(',')]);
25
26 return metrics;
27}Connection Management#
Reconnection Logic#
Implement robust WebSocket reconnection:
Requirements:
- Exponential backoff
- Maximum retry limit
- Connection state tracking
- Resubscribe on reconnect
- Handle network changes (online/offline events)
```typescript
class ReconnectingWebSocket {
// Implementation needed
}
Provide complete implementation.
### Heartbeat/Ping-Pong
```typescript
class WebSocketClient {
private ws: WebSocket;
private heartbeatInterval: NodeJS.Timer;
private heartbeatTimeout: NodeJS.Timer;
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
if (event.data === 'pong') {
this.resetHeartbeatTimeout();
return;
}
this.handleMessage(event);
};
}
private startHeartbeat() {
this.heartbeatInterval = setInterval(() => {
this.ws.send('ping');
this.heartbeatTimeout = setTimeout(() => {
this.ws.close();
this.reconnect();
}, 5000);
}, 30000);
}
private resetHeartbeatTimeout() {
clearTimeout(this.heartbeatTimeout);
}
}
Scaling Real-Time Systems#
Horizontal Scaling with Redis#
Design a scalable WebSocket architecture:
Requirements:
- 100K concurrent connections
- Multiple server instances behind load balancer
- Message delivery to correct server/connection
- Pub/sub for broadcast messages
Architecture:
- Sticky sessions or Redis for connection registry
- Redis pub/sub for cross-server messaging
- Room/channel abstraction
Provide implementation with Socket.io and Redis adapter.
Connection Distribution#
┌─────────────────────────────────────────────┐
│ Load Balancer │
│ (sticky sessions or L4) │
└─────────────────┬───────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ WS 1 │ │ WS 2 │ │ WS 3 │
│ 33K │ │ 33K │ │ 33K │
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
└────────────┼────────────┘
▼
┌───────────┐
│ Redis │
│ Pub/Sub │
└───────────┘
Security Considerations#
Authentication#
Implement secure WebSocket authentication:
Options:
1. Token in connection URL (risky - logged)
2. Token in first message (delays)
3. Cookie-based (requires same origin)
4. Token in header (not always possible)
Recommend approach for:
- Browser clients
- Mobile clients
- Server-to-server
Include token refresh handling.
Rate Limiting#
1// Per-connection rate limiting
2class RateLimiter {
3 private counts = new Map<string, number>();
4
5 isAllowed(connectionId: string, limit: number, window: number): boolean {
6 const now = Date.now();
7 const key = `${connectionId}:${Math.floor(now / window)}`;
8
9 const count = (this.counts.get(key) || 0) + 1;
10 this.counts.set(key, count);
11
12 // Clean old windows
13 this.cleanup(now, window);
14
15 return count <= limit;
16 }
17
18 private cleanup(now: number, window: number) {
19 const threshold = Math.floor(now / window) - 1;
20 for (const key of this.counts.keys()) {
21 const [, windowKey] = key.split(':');
22 if (parseInt(windowKey) < threshold) {
23 this.counts.delete(key);
24 }
25 }
26 }
27}Testing Real-Time Features#
Unit Testing#
Generate tests for this WebSocket handler:
```typescript
class ChatHandler {
async handleMessage(socket: Socket, message: ChatMessage) {
// Validate message
// Store in database
// Broadcast to room
// Send delivery confirmation
}
}
Include tests for:
- Message validation
- Database errors
- Broadcast failures
- Concurrent messages
### Load Testing
Design a load test for WebSocket server:
Target:
- 10K concurrent connections
- 100 messages/second per connection
- 99th percentile latency < 100ms
Tools: Artillery, k6, or custom
Provide:
- Test scenario
- Metrics to collect
- Success criteria
## Frontend Patterns
### React Hook for WebSocket
```typescript
function useWebSocket<T>(url: string, options: WebSocketOptions = {}) {
const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected'>('connecting');
const [lastMessage, setLastMessage] = useState<T | null>(null);
const wsRef = useRef<WebSocket | null>(null);
const send = useCallback((data: unknown) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
}
}, []);
useEffect(() => {
const ws = new WebSocket(url);
wsRef.current = ws;
ws.onopen = () => setStatus('connected');
ws.onclose = () => setStatus('disconnected');
ws.onmessage = (e) => setLastMessage(JSON.parse(e.data));
return () => ws.close();
}, [url]);
return { status, lastMessage, send };
}
Optimistic Updates#
1function useChatRoom(roomId: string) {
2 const [messages, setMessages] = useState<Message[]>([]);
3 const { send, lastMessage } = useWebSocket(`/ws/rooms/${roomId}`);
4
5 // Handle incoming messages
6 useEffect(() => {
7 if (lastMessage) {
8 setMessages(prev => {
9 // Replace optimistic message with confirmed one
10 const filtered = prev.filter(m => m.id !== lastMessage.tempId);
11 return [...filtered, lastMessage];
12 });
13 }
14 }, [lastMessage]);
15
16 const sendMessage = (content: string) => {
17 const tempId = generateTempId();
18 const optimisticMessage = {
19 id: tempId,
20 content,
21 status: 'sending',
22 timestamp: new Date()
23 };
24
25 // Optimistic update
26 setMessages(prev => [...prev, optimisticMessage]);
27
28 // Send to server
29 send({ type: 'message', content, tempId });
30 };
31
32 return { messages, sendMessage };
33}Conclusion#
Real-time features add complexity but transform user experience. With proper patterns for connection management, scaling, and security, you can build reliable real-time systems.
AI assists at every step—choosing technologies, implementing patterns, handling edge cases, and testing under load. Start with simple SSE for one-way updates, graduate to WebSockets when you need bidirectional communication, and scale with Redis when you outgrow a single server.