Reconnection

WebSocket connections drop. Network blips, mobile backgrounding, server restarts — production code has to handle reconnection gracefully.

Why reconnection matters

On the public internet, your connection has a ~99% chance of being killed within a few hours. A long-running consumer needs both reconnection and state-resume logic.

Exponential backoff

Don't hammer the server. Start at 1s, double each attempt, cap at 60s, add jitter.

function nextDelay(attempt) {
  const exp = Math.min(2 ** attempt, 60);
  const jitter = Math.random() * 0.3 * exp;
  return (exp + jitter) * 1000;
}

Resuming state

On reconnect:

  1. Re-authenticate.
  2. Re-subscribe to every channel you had before.
  3. For data where you might have missed updates, do a one-shot REST call to catch up.
💡
The server does not remember client state — it's your responsibility to track what you're subscribed to and re-issue subscriptions on reconnect.

Production-ready code

class SportapiStream {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.subs = new Set();
    this.attempt = 0;
    this.handlers = {};
    this.connect();
  }

  connect() {
    this.ws = new WebSocket('wss://stream.sportapi.io/v1');
    this.ws.onopen = () => {
      this.attempt = 0;
      this.ws.send(JSON.stringify({ type: 'auth', token: this.apiKey }));
      for (const ch of this.subs) {
        this.ws.send(JSON.stringify({ type: 'subscribe', channel: ch }));
      }
    };
    this.ws.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      if (msg.type === 'update') {
        (this.handlers[msg.channel] || []).forEach(h => h(msg.data));
      }
    };
    this.ws.onclose = () => this.scheduleReconnect();
    this.ws.onerror = () => this.ws.close();
  }

  scheduleReconnect() {
    const delay = Math.min(2 ** this.attempt, 60) * 1000;
    this.attempt++;
    setTimeout(() => this.connect(), delay + Math.random() * 1000);
  }

  subscribe(channel, handler) {
    this.subs.add(channel);
    this.handlers[channel] = (this.handlers[channel] || []).concat(handler);
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type: 'subscribe', channel }));
    }
  }
}