Skip to main content

Overview

Protocol: WebSocket (RFC 6455) Communication: Bidirectional (Client ↔ Server) Endpoint: wss://api.petstoreapi.com/v1/ws/chat When to Use WebSocket:
  • ✅ Real-time chat applications
  • ✅ Collaborative editing
  • ✅ Live dashboards and monitoring
  • ✅ Gaming and interactive applications
  • ✅ Bidirectional communication required
When NOT to Use WebSocket:
  • ❌ Only need server → client updates (use SSE instead)
  • ❌ Simple request/response (use REST instead)

How WebSocket Works

Client                                    Server
  │                                         │
  ├───────── HTTP Upgrade (WebSocket) ────>│
  │<──────────── 101 Switching Protocols ──┤
  │                                         │
  │<───────────── Message 1 ───────────────┤
  ├───────────── Message 2 ───────────────>│
  │<───────────── Message 3 ───────────────┤
  ├───────────── Message 4 ───────────────>│
  │          (persistent connection)        │
Key Characteristics:
  • Full-duplex communication (send and receive simultaneously)
  • Low latency overhead
  • Persistent connection
  • Binary and text data support
  • No HTTP overhead after handshake

Connection

URL Format

wss://api.petstoreapi.com/v1/ws/chat?token=YOUR_JWT_TOKEN
│   │                            │           │
│   │                            │           └─ Query params (auth)
│   │                            └────────────── Path
│   └────────────────────────────────────────── Host
└────────────────────────────────────────────── Protocol (wss = secure)

JavaScript (Browser)

// Connect with authentication token
const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws/chat?token=YOUR_JWT_TOKEN');

// Connection opened
ws.addEventListener('open', (event) => {
  console.log('Connected to WebSocket');
});

// Listen for messages
ws.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);
  console.log('Received:', message);
});

// Connection closed
ws.addEventListener('close', (event) => {
  console.log('Disconnected:', event.code, event.reason);
});

// Handle errors
ws.addEventListener('error', (event) => {
  console.error('WebSocket error:', event);
});

// Send a message
ws.send(JSON.stringify({
  type: 'chatMessage',
  payload: {
    conversationId: 'conv_abc123',
    senderId: 'user_xyz789',
    content: 'Hello, I need help with my order',
    timestamp: new Date().toISOString()
  }
}));

JavaScript (Node.js - ws library)

const WebSocket = require('ws');

const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws/chat?token=YOUR_JWT_TOKEN');

ws.on('open', () => {
  console.log('Connected');

  // Send a message
  ws.send(JSON.stringify({
    type: 'chatMessage',
    payload: {
      conversationId: 'conv_abc123',
      content: 'Hello from Node.js'
    }
  }));
});

ws.on('message', (data) => {
  const message = JSON.parse(data);
  console.log('Received:', message);
});

ws.on('close', (code, reason) => {
  console.log('Disconnected:', code, reason);
});

ws.on('error', (error) => {
  console.error('Error:', error);
});

Python (websockets)

import asyncio
import websockets
import json

async def chat_client():
    uri = "wss://api.petstoreapi.com/v1/ws/chat?token=YOUR_JWT_TOKEN"

    async with websockets.connect(uri) as websocket:
        # Send a message
        await websocket.send(json.dumps({
            "type": "chatMessage",
            "payload": {
                "conversationId": "conv_abc123",
                "content": "Hello from Python"
            }
        }))

        # Receive messages
        async for message in websocket:
            data = json.loads(message)
            print(f"Received: {data['type']}")

            if data['type'] == 'chatMessage':
                print(f"Message: {data['payload']['content']}")

asyncio.run(chat_client())

Message Types

Chat Message

Send:
{
  "type": "chatMessage",
  "payload": {
    "conversationId": "conv_abc123",
    "senderId": "user_xyz789",
    "content": "Hello, I need help with my order",
    "timestamp": "2025-01-06T12:00:00Z"
  }
}
Receive:
{
  "type": "chatMessage",
  "payload": {
    "conversationId": "conv_abc123",
    "messageId": "msg_xyz789",
    "senderId": "agent_abc123",
    "senderName": "Support Agent",
    "content": "Hi! How can I help you today?",
    "timestamp": "2025-01-06T12:00:01Z"
  }
}

Message Delivered

{
  "type": "messageDelivered",
  "payload": {
    "conversationId": "conv_abc123",
    "messageId": "msg_xyz789",
    "timestamp": "2025-01-06T12:00:02Z"
  }
}

Message Read

{
  "type": "messageRead",
  "payload": {
    "conversationId": "conv_abc123",
    "messageId": "msg_xyz789",
    "readBy": "user_xyz789",
    "timestamp": "2025-01-06T12:00:03Z"
  }
}

Typing Indicators

Start Typing:
{
  "type": "typingStarted",
  "payload": {
    "conversationId": "conv_abc123",
    "userId": "user_xyz789"
  }
}
Stop Typing:
{
  "type": "typingStopped",
  "payload": {
    "conversationId": "conv_abc123",
    "userId": "user_xyz789"
  }
}

Agent Assigned

{
  "type": "agentAssigned",
  "payload": {
    "conversationId": "conv_abc123",
    "agentId": "agent_abc123",
    "agentName": "Sarah Johnson",
    "timestamp": "2025-01-06T12:00:00Z"
  }
}

Advanced Usage

Reconnection Logic

class ReconnectingWebSocket {
  constructor(url, maxRetries = 5) {
    this.url = url;
    this.maxRetries = maxRetries;
    this.retryCount = 0;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('Connected');
      this.retryCount = 0;
    };

    this.ws.onclose = (event) => {
      if (this.retryCount < this.maxRetries) {
        const delay = Math.min(1000 * Math.pow(2, this.retryCount), 30000);
        setTimeout(() => {
          this.retryCount++;
          this.connect();
        }, delay);
      }
    };
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    }
  }
}

Heartbeat/Ping-Pong

const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws/chat?token=YOUR_TOKEN');

// Send ping every 30 seconds
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 30000);

// Handle pong
ws.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'pong') {
    console.log('Pong received - connection alive');
  }
});

Message Queue

class MessageQueue {
  constructor(ws) {
    this.ws = ws;
    this.queue = [];
    this.isReady = false;

    this.ws.onopen = () => {
      this.isReady = true;
      this.flush();
    };
  }

  send(message) {
    if (this.isReady) {
      this.ws.send(JSON.stringify(message));
    } else {
      this.queue.push(message);
    }
  }

  flush() {
    while (this.queue.length > 0 && this.isReady) {
      const message = this.queue.shift();
      this.ws.send(JSON.stringify(message));
    }
  }
}

Authentication

Query Parameter

const token = 'your_jwt_token';
const ws = new WebSocket(`wss://api.petstoreapi.com/v1/ws/chat?token=${token}`);

Subprotocol

const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws/chat', ['petstore-chat', {
  protocol: 'bearer',
  token: 'your_jwt_token'
}]);

Error Handling

Connection Errors

ws.addEventListener('error', (event) => {
  console.error('WebSocket error:', event);

  // Check specific error types
  if (event.type === 'error') {
    console.error('Connection failed');
  }
});

Close Codes

ws.addEventListener('close', (event) => {
  console.log(`Connection closed: ${event.code} - ${event.reason}`);

  switch (event.code) {
    case 1000:
      console.log('Normal closure');
      break;
    case 1001:
      console.log('Endpoint going away');
      break;
    case 1002:
      console.log('Protocol error');
      break;
    case 1003:
      console.log('Unsupported data');
      break;
    case 1008:
      console.log('Policy violation');
      break;
    default:
      console.log('Unknown close code');
  }
});

Best Practices

1. Graceful Shutdown

window.addEventListener('beforeunload', () => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'goodbye' }));
    ws.close(1000, 'Page closing');
  }
});

2. Message Validation

function sendMessage(message) {
  const validated = validateMessage(message);
  if (validated.error) {
    console.error('Invalid message:', validated.error);
    return;
  }

  ws.send(JSON.stringify(validated.data));
}

3. Rate Limiting

class RateLimitedWebSocket {
  constructor(ws, messagesPerSecond = 10) {
    this.ws = ws;
    this.minInterval = 1000 / messagesPerSecond;
    this.lastSend = 0;
  }

  send(message) {
    const now = Date.now();
    const elapsed = now - this.lastSend;

    if (elapsed >= this.minInterval) {
      this.ws.send(JSON.stringify(message));
      this.lastSend = now;
    } else {
      console.warn('Rate limited');
    }
  }
}

Troubleshooting

Connection Drops

  • Implement reconnection logic with exponential backoff
  • Check network connectivity
  • Verify authentication token is valid
  • Monitor close codes and reasons

Memory Leaks

  • Clean up event listeners when closing
  • Don’t accumulate message history indefinitely
  • Use weak references where appropriate

High CPU Usage

  • Avoid tight loops when sending messages
  • Use requestAnimationFrame for UI updates
  • Batch message processing

Comparison with Alternatives

FeatureWebSocketSSEPolling
DirectionBidirectionalServer → ClientClient → Server
LatencyVery LowLowHigh
OverheadVery LowLowHigh
Binary Support
Browser SupportExcellentExcellentUniversal
ReconnectionManualAutomaticN/A

Interactive Documentation