NATS WebSocket
Direct connection to NATS message broker via WebSocket, bypassing the backend entirely for ultra-low latency streaming.
Open NATS Client — See direct broker connection in action!
Overview
NATS WebSocket provides direct client-to-broker connectivity using the native NATS WebSocket interface. This eliminates the backend hop, achieving the lowest possible latency for real-time data.
Connection Details
| Property | Value |
|---|---|
| Protocol | WebSocket (NATS native) |
| Port | 8443 |
| Latency | 0.5-2ms |
| Direction | Bidirectional |
| Library | nats.ws (ESM) |
Architecture
PostgreSQL → Debezium → NATS JetStream → Client (direct)
↑
WebSocket :8443
Unlike other protocols that go through the backend, NATS WebSocket connects directly to the message broker:
- No Backend Hop: Client subscribes directly to NATS subjects
- Native Protocol: Uses nats.ws library for full NATS functionality
- Subject Patterns: Subscribe to specific games or wildcards
Subject Patterns
NATS organizes messages using subjects (topics):
rounds.game.crash # Crash game results only
rounds.game.double # Double game results only
rounds.game.wall-street # Wall Street game results only
rounds.game.slot # Slot game results only
rounds.game.* # All games (wildcard)
rounds.type.multiplier # All multiplier-type games
rounds.type.color # All color-type games
Client Implementation
JavaScript (Browser)
import { connect, StringCodec } from 'nats.ws';
async function connectToNATS() {
// Connect to NATS WebSocket
const nc = await connect({
servers: 'ws://localhost:8443'
});
console.log('Connected to NATS');
const sc = StringCodec();
// Subscribe to all game results
const sub = nc.subscribe('rounds.game.*');
for await (const msg of sub) {
const data = JSON.parse(sc.decode(msg.data));
const subject = msg.subject;
const game = subject.split('.').pop();
console.log(`[${game}]`, data);
handleResult(game, data);
}
}
function handleResult(game, result) {
const extras = typeof result.extras === 'string'
? JSON.parse(result.extras)
: result.extras;
console.log(`Game: ${game}, Round: ${result.round_id}`);
console.log(`Result:`, extras);
}
connectToNATS().catch(console.error);
Using ESM Import
<script type="module">
import { connect, StringCodec } from 'https://esm.sh/nats.ws@1.19.1';
const nc = await connect({ servers: 'ws://localhost:8443' });
const sc = StringCodec();
// Subscribe to crash game only
const sub = nc.subscribe('rounds.game.crash');
for await (const msg of sub) {
const result = JSON.parse(sc.decode(msg.data));
updateUI(result);
}
</script>
Message Format
Messages received from NATS have this structure:
{
"id": 12345,
"game_slug": "crash",
"game_type": "multiplier",
"round_id": 67890,
"started_at": "2024-01-15T10:30:00Z",
"finished_at": "2024-01-15T10:30:15Z",
"extras": {
"point": "2.47"
}
}
Game-Specific Extras
Crash (multiplier):
{ "point": "2.47" }
Double (color):
{ "color": "red", "number": 7 }
Wall Street (trending):
{ "trending": "up", "multiplier": "1.85" }
Slot (slots):
{ "reels": ["7", "7", "BAR"], "multiplier": "3.50" }
Latency Tracking
Track end-to-end latency by comparing timestamps:
for await (const msg of sub) {
const receiveTime = Date.now();
const result = JSON.parse(sc.decode(msg.data));
// If message includes timestamp
if (result.timestamp) {
const latency = receiveTime - result.timestamp;
console.log(`Latency: ${latency}ms`);
}
}
Connection Management
Reconnection Handling
const nc = await connect({
servers: 'ws://localhost:8443',
reconnect: true,
maxReconnectAttempts: -1, // Infinite retries
reconnectTimeWait: 1000, // 1 second between attempts
});
// Connection events
nc.closed().then(() => {
console.log('Connection closed');
});
// Status updates
(async () => {
for await (const status of nc.status()) {
console.log(`Status: ${status.type}`);
}
})();
Graceful Shutdown
// Drain subscriptions before closing
await nc.drain();
// Or force close
await nc.close();
When to Use NATS WebSocket
Best For
- Ultra-low latency requirements (< 2ms)
- High-frequency updates (100+ msgs/sec)
- Direct broker access without backend
- Flexible subscriptions with wildcard patterns
- Multiple game monitoring simultaneously
Considerations
- Requires NATS WebSocket port (8443) accessible
- Client manages reconnection logic
- No HTTP fallback if WebSocket fails
- Browser must support ES modules
Comparison with Other Protocols
| Aspect | NATS WS | WebSocket | SSE | REST |
|---|---|---|---|---|
| Latency | 0.5-2ms | 1-5ms | 10-50ms | 50-200ms |
| Direction | Bidirectional | Bidirectional | Server→Client | Request/Response |
| Wildcards | Yes | No | No | No |
| Direct Broker | Yes | No | No | No |
| Auto-reconnect | Configurable | Manual | Native | N/A |
Live Demo
Try the NATS WebSocket demo at /nats.html:
http://localhost:3000/nats.html
Features:
- Real-time connection to NATS
- Wildcard subscription to all games
- Latency measurement per message
- Message count and statistics
- Connection status indicator
Troubleshooting
Connection Refused
Error: connection refused
Ensure NATS is running with WebSocket enabled:
# Check NATS is running
docker compose ps nats
# Verify WebSocket port
curl -I http://localhost:8443
Invalid Subject
Error: invalid subject
Subjects must follow NATS naming conventions:
- No spaces
- Alphanumeric with dots (
.) and wildcards (*,>)
CORS Issues
NATS WebSocket typically doesn't have CORS restrictions, but if running behind a proxy:
location /nats {
proxy_pass http://nats:8443;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}