Skip to main content

NATS WebSocket

Direct connection to NATS message broker via WebSocket, bypassing the backend entirely for ultra-low latency streaming.

Try it Online

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.

Protocol Connection

Connection Details

PropertyValue
ProtocolWebSocket (NATS native)
Port8443
Latency0.5-2ms
DirectionBidirectional
Librarynats.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

AspectNATS WSWebSocketSSEREST
Latency0.5-2ms1-5ms10-50ms50-200ms
DirectionBidirectionalBidirectionalServer→ClientRequest/Response
WildcardsYesNoNoNo
Direct BrokerYesNoNoNo
Auto-reconnectConfigurableManualNativeN/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";
}