Why WebSockets Over HTTP Polling?
Traditional HTTP polling wastes bandwidth and adds latency:
- HTTP Polling: Client requests updates every 1-5 seconds (even when no data changed)
- Long Polling: Server holds request open until data available (complex, resource-intensive)
- WebSockets: Persistent connection, instant bidirectional messages (under 50ms latency)
Performance Comparison
Scenario: 1,000 concurrent users, 1 update per minute
HTTP Polling (1s interval):
Requests: 60,000/minute (1,000 users x 60 polls)
Data transferred: ~12 MB/min (overhead)
Server load: High (constant request processing)
Latency: 0-1000ms (depends on poll timing)
WebSockets:
Requests: 1,000 connections (persistent)
Data transferred: ~50 KB/min (only actual updates)
Server load: Low (idle connections)
Latency: under 50ms (instant push)
Result: 99.6% reduction in server requestsWebSocket Architecture on AWS
1. AWS API Gateway WebSocket API
Managed WebSocket infrastructure with auto-scaling:
# Create WebSocket API
aws apigatewayv2 create-api \
--name "SnapIT-Chat-WebSocket" \
--protocol-type WEBSOCKET \
--route-selection-expression '$request.body.action'
# Routes:
# $connect -> onConnect Lambda (authenticate user)
# $disconnect -> onDisconnect Lambda (cleanup)
# sendMessage -> sendMessage Lambda (broadcast to users)
# $default -> default Lambda (error handling)2. Connection Management with DynamoDB
Store active connections for message broadcasting:
// DynamoDB Table: WebSocketConnections
{
connectionId: "abc123def456", // Primary Key
userId: "user_789",
roomId: "room_general",
connectedAt: 1697654400000,
ttl: 1697740800 // Auto-delete after 24 hours
}
// onConnect Lambda
exports.handler = async (event) => {
const connectionId = event.requestContext.connectionId
const userId = event.queryStringParameters.userId
await dynamodb.put({
TableName: 'WebSocketConnections',
Item: {
connectionId,
userId,
connectedAt: Date.now(),
ttl: Math.floor(Date.now() / 1000) + 86400
}
}).promise()
return { statusCode: 200, body: 'Connected' }
}Building a Real-Time Chat App
Frontend: React WebSocket Client
import { useEffect, useState } from 'react'
const ChatApp = ({ userId, roomId }) => {
const [ws, setWs] = useState(null)
const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
useEffect(() => {
// Connect to WebSocket API
const wsUrl = 'wss://your-api-id.execute-api.us-east-1.amazonaws.com/prod'
+ '?userId=' + userId + '&roomId=' + roomId
const socket = new WebSocket(wsUrl)
socket.onopen = () => console.log('Connected')
socket.onmessage = (event) => {
const message = JSON.parse(event.data)
setMessages(prev => [...prev, message])
}
socket.onerror = (error) => console.error('WebSocket error:', error)
socket.onclose = () => console.log('Disconnected')
setWs(socket)
return () => socket.close()
}, [userId, roomId])
const sendMessage = () => {
ws.send(JSON.stringify({
action: 'sendMessage',
roomId,
userId,
message: input,
timestamp: Date.now()
}))
setInput('')
}
return (
<div>
<div className="messages">
{messages.map((msg, i) => (
<div key={i}>{msg.userId}: {msg.message}</div>
))}
</div>
<input value={input} onChange={e => setInput(e.target.value)} />
<button onClick={sendMessage}>Send</button>
</div>
)
}Backend: Message Broadcasting Lambda
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB.DocumentClient()
const apiGateway = new AWS.ApiGatewayManagementApi({
endpoint: process.env.WEBSOCKET_ENDPOINT
})
exports.handler = async (event) => {
const { roomId, userId, message } = JSON.parse(event.body)
// Get all connections in room
const connections = await dynamodb.query({
TableName: 'WebSocketConnections',
IndexName: 'roomId-index',
KeyConditionExpression: 'roomId = :roomId',
ExpressionAttributeValues: { ':roomId': roomId }
}).promise()
// Broadcast message to all room members
const postToConnection = async (connectionId) => {
try {
await apiGateway.postToConnection({
ConnectionId: connectionId,
Data: JSON.stringify({ userId, message, timestamp: Date.now() })
}).promise()
} catch (error) {
if (error.statusCode === 410) {
// Connection stale, remove from DB
await dynamodb.delete({
TableName: 'WebSocketConnections',
Key: { connectionId }
}).promise()
}
}
}
await Promise.all(
connections.Items.map(conn => postToConnection(conn.connectionId))
)
return { statusCode: 200, body: 'Message sent' }
}SnapIT Real-Time Infrastructure
Deploy production-ready WebSocket chat, notifications, and collaboration features in minutes. Pre-built AWS CDK stacks with API Gateway, Lambda, and DynamoDB.
Explore Real-Time SolutionsAdvanced Use Cases
1. Live Notifications
Push alerts to users instantly:
- New order notifications (e-commerce)
- Friend requests (social networks)
- System alerts (SaaS dashboards)
- Price drop alerts (price tracking)
2. Collaborative Editing
Real-time document collaboration like Google Docs:
// Operational Transformation (OT) for conflict resolution
const applyOperation = (doc, operation) => {
switch (operation.type) {
case 'insert':
return doc.slice(0, operation.position) +
operation.text +
doc.slice(operation.position)
case 'delete':
return doc.slice(0, operation.position) +
doc.slice(operation.position + operation.length)
}
}
// Broadcast cursor positions
ws.send(JSON.stringify({
action: 'cursorMove',
userId,
position: { line: 5, column: 12 },
color: '#db2777'
}))3. Live Dashboards
Real-time metrics for analytics platforms:
- Active users count (updates every second)
- Revenue tracking (live sales notifications)
- System health monitoring (server metrics)
- Social media engagement (likes, shares in real-time)
Security & Best Practices
1. Authentication
// Validate JWT token in $connect route
const jwt = require('jsonwebtoken')
exports.handler = async (event) => {
const token = event.queryStringParameters.token
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
// Store userId from decoded token
await storeConnection(event.requestContext.connectionId, decoded.userId)
return { statusCode: 200 }
} catch (error) {
return { statusCode: 401, body: 'Unauthorized' }
}
}2. Rate Limiting
Prevent abuse with message rate limits:
- Max 10 messages per second per user
- Max 1,000 connections per IP
- DynamoDB TTL for auto-cleanup of stale connections
3. Error Handling
- Automatic reconnection with exponential backoff
- Message queue for offline users (store in DynamoDB)
- Dead letter queue (DLQ) for failed broadcasts
Cost Analysis
Scenario: Chat app with 10,000 concurrent users, 1 million messages per day
- API Gateway WebSocket: $1.00/million messages + $0.25/million connection minutes = $3.47/day
- Lambda executions: 1M invocations x 200ms x 512MB = $1.67/day
- DynamoDB: 1M writes, 10M reads = $2.75/day
- Total: $7.89/day ($236/month for 10K users = $0.024 per user/month)
Conclusion
WebSockets transform user experience with instant updates and real-time collaboration. AWS API Gateway + Lambda + DynamoDB provides a scalable, cost-effective infrastructure for chat apps, live notifications, and collaborative tools. With sub-50ms latency and 99.6% fewer server requests than HTTP polling, WebSockets are essential for modern interactive applications.