Skip to content

WebSocket Clients: JavaScript

This guide explains how to create, manage, and secure a WebSocket client in modern JavaScript. It includes connection setup, message handling, reconnection logic, and best practices for real-time apps.


πŸš€ 1. Basic WebSocket Client

const socket = new WebSocket("wss://your-api.com/ws");

socket.onopen = () => {
  console.log("Connected to WebSocket");
};

socket.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  handleMessage(msg);
};

socket.onclose = (event) => {
  console.log("Disconnected:", event.code, event.reason);
};

socket.onerror = (error) => {
  console.error("WebSocket error:", error);
};

πŸ“¨ 2. Sending Messages

Always follow your backend’s message format (e.g. include type and data).

function sendMessage(type, data) {
  const message = {
    type,
    data,
    meta: {
      request_id: crypto.randomUUID(),
      timestamp: new Date().toISOString(),
    },
  };
  socket.send(JSON.stringify(message));
}

Example:

sendMessage("send_message", {
  room_id: "chat-room-1",
  message: "Hello world!",
});

πŸ” 3. Reconnection Logic

Add automatic reconnection with exponential backoff.

let reconnectAttempts = 0;

function connect() {
  const ws = new WebSocket("wss://your-api.com/ws?token=YOUR_TOKEN");

  ws.onopen = () => {
    reconnectAttempts = 0;
    console.log("WebSocket connected");
  };

  ws.onclose = (event) => {
    console.warn("WebSocket closed:", event.code);
    const timeout = Math.min(1000 * 2 ** reconnectAttempts, 30000);
    setTimeout(connect, timeout);
    reconnectAttempts++;
  };

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    handleMessage(msg);
  };

  ws.onerror = (e) => console.error("WebSocket error:", e);
}

connect();

πŸ” 4. Authentication

Most secure apps use a token in the query string or send an auth message after connection:

With token in URL:

const token = getTokenFromStorage();
const socket = new WebSocket(`wss://your-api.com/ws?token=${token}`);

Or: send token in first message:

socket.onopen = () => {
  sendMessage("auth", { token });
};

🧠 5. Handling Server Messages

Structure your message handler based on type.

function handleMessage(msg) {
  switch (msg.type) {
    case "connection_established":
      console.log("Connected:", msg.data);
      break;
    case "new_message":
      displayChatMessage(msg.data);
      break;
    case "user_typing":
      showTypingIndicator(msg.data.user_id);
      break;
    case "error":
      alert("Error: " + msg.data.message);
      break;
    default:
      console.warn("Unknown event type:", msg.type);
  }
}

πŸ§ͺ 6. Debugging Tips

  • Use browser DevTools β†’ Network > WS tab to inspect messages.
  • Log all onmessage and onerror events during development.
  • Retry connections only after handling auth or network errors properly.
  • Throttle reconnection attempts to avoid flooding the server.

βœ… 7. Best Practices

  • Always use wss:// in production for encryption.
  • Abstract socket logic into a reusable class or hook.
  • Handle all close codes gracefully (e.g., 1008 for auth failure).
  • Implement ping/pong logic if required by the backend.
  • Track connection state for UI updates (e.g., loading spinners, reconnect notices).

πŸ“¦ 8. Optional: WebSocket Wrapper Class

class WSClient {
  constructor(url) {
    this.url = url;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);
    this.ws.onopen = () => console.log("Connected");
    this.ws.onclose = () => setTimeout(() => this.connect(), 3000);
    this.ws.onmessage = (e) => this.onMessage(JSON.parse(e.data));
    this.ws.onerror = (e) => console.error(e);
  }

  send(type, data) {
    this.ws.send(JSON.stringify({ type, data }));
  }

  onMessage(msg) {
    console.log("Received:", msg);
    // Dispatch to handlers here
  }
}

πŸ”š Next Steps