WebSocket API
Real-time dashboard updates via Socket.IO WebSocket gateways.
Deployment Note
WebSocket gateways are NOT available on AWS Lambda deployments. They require the analytics-svc to be deployed on ECS Fargate or run locally via pnpm dev. The Lambda deployment provides REST and GraphQL endpoints only.
Gateways
| Gateway | Namespace | Purpose |
|---|---|---|
DashboardGateway | /dashboard | Real-time dashboard metric updates |
DashboardUpdatesGateway | /analytics/dashboard | Dashboard configuration change notifications |
Connection
Connect with socket.io-clienttypescript
import { io } from 'socket.io-client';
const socket = io('http://localhost:4004/dashboard', {
auth: {
token: '<JWT-access-token>',
},
transports: ['websocket'],
});
socket.on('connect', () => {
console.log('Connected to analytics dashboard');
});
socket.on('connect_error', (err) => {
console.error('Connection failed:', err.message);
});Authentication
Authentication is performed via the auth.token field in the Socket.IO handshake. The JWT token is validated on connection and the tenantId is extracted from claims to scope all data.
Authentication handshaketypescript
const socket = io('http://localhost:4004/dashboard', {
auth: {
token: 'eyJhbGciOiJSUzI1NiIs...',
},
});Client Events (Emit)
| Event | Payload | Description |
|---|---|---|
subscribe | { entityId, entityType } | Subscribe to real-time updates for an entity |
unsubscribe | { entityId } | Unsubscribe from entity updates |
Subscribe to entity updatestypescript
// Subscribe to real-time metrics for an event
socket.emit('subscribe', {
entityId: 'event-uuid-123',
entityType: 'event',
});
// Unsubscribe when leaving dashboard
socket.emit('unsubscribe', {
entityId: 'event-uuid-123',
});Server Events (Listen)
| Event | Description |
|---|---|
dashboard:initial | Initial dashboard state sent after subscribing |
dashboard:update | Dashboard configuration changed (tiles added/removed/reordered) |
metric:update | Real-time metric value update for a subscribed entity |
error | Error notification (invalid entity, permission denied, etc.) |
Listen for server eventstypescript
// Receive initial dashboard state
socket.on('dashboard:initial', (data) => {
console.log('Dashboard loaded:', data);
// data: { entityId, entityType, metrics: { ... }, tiles: [...] }
});
// Receive real-time metric updates
socket.on('metric:update', (data) => {
console.log('Metric updated:', data);
// data: { entityId, metricName, value, timestamp }
});
// Receive dashboard config changes
socket.on('dashboard:update', (data) => {
console.log('Dashboard changed:', data);
// data: { dashboardId, action, tile: { ... } }
});
// Handle errors
socket.on('error', (err) => {
console.error('WebSocket error:', err);
// err: { code: 'ANALYTICS_1100', message: '...' }
});Full Example
Complete WebSocket integrationtypescript
import { io } from 'socket.io-client';
const token = '<your-jwt-token>';
const socket = io('http://localhost:4004/dashboard', {
auth: { token },
transports: ['websocket'],
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
});
socket.on('connect', () => {
// Subscribe to event metrics
socket.emit('subscribe', {
entityId: 'event-uuid-123',
entityType: 'event',
});
});
socket.on('dashboard:initial', (data) => {
// Render initial dashboard
renderDashboard(data);
});
socket.on('metric:update', (data) => {
// Update specific metric in real-time
updateMetricTile(data.metricName, data.value);
});
socket.on('disconnect', (reason) => {
console.log('Disconnected:', reason);
});
// Cleanup on component unmount
function cleanup() {
socket.emit('unsubscribe', { entityId: 'event-uuid-123' });
socket.disconnect();
}