Skip to content

Advanced Techniques with @briklab/slikr

Once comfortable with the basics, these patterns help you build robust real-time applications.

Custom Retry Strategies

ts
import slikr from "@briklab/slikr";

const client = slikr("wss://api.example.com");

// Exponential backoff with jitter
client.retryDelayIncreaseFn(current => {
  const jitter = Math.random() * 0.1 * current; // Add up to 10% jitter
  return current * 2 + jitter;
});

// Fibonacci backoff
let prev = 1000;
let curr = 1000;
client.retryDelayIncreaseFn(() => {
  const next = prev + curr;
  prev = curr;
  curr = next;
  return curr;
});

Connection State Management

ts
import slikr from "@briklab/slikr";

class ConnectionManager {
  private client: ReturnType<typeof slikr>;
  private reconnectAttempts = 0;
  private maxReconnects = 10;

  constructor(url: string) {
    this.client = slikr(url);
    this.setupEventHandlers();
  }

  private setupEventHandlers() {
    this.client.on("close", () => this.handleDisconnect());
    this.client.on("open", () => {
      console.log("Connected");
      this.reconnectAttempts = 0;
    });
  }

  private async handleDisconnect() {
    if (this.reconnectAttempts >= this.maxReconnects) {
      console.error("Max reconnection attempts reached");
      return;
    }

    this.reconnectAttempts++;
    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);

    console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);

    setTimeout(async () => {
      try {
        await this.client.connect();
      } catch (error) {
        console.error("Reconnection failed:", error.message);
      }
    }, delay);
  }

  async connect() {
    await this.client.connect();
  }

  getClient() {
    return this.client;
  }
}

Message Queuing and Offline Support

ts
import slikr from "@briklab/slikr";

class QueuedClient {
  private client: ReturnType<typeof slikr>;
  private queue: Array<{ event: string; data: any }> = [];
  private connected = false;

  constructor(url: string) {
    this.client = slikr(url);
    this.client.on("open", () => {
      this.connected = true;
      this.flushQueue();
    });
    this.client.on("close", () => {
      this.connected = false;
    });
  }

  async send(event: string, data: any) {
    if (this.connected) {
      await this.client.send(event, data);
    } else {
      this.queue.push({ event, data });
    }
  }

  private async flushQueue() {
    while (this.queue.length > 0) {
      const message = this.queue.shift()!;
      try {
        await this.client.send(message.event, message.data);
      } catch (error) {
        console.error("Failed to send queued message:", error);
        // Optionally re-queue or handle error
      }
    }
  }

  async connect() {
    await this.client.connect();
  }
}

Binary Data Handling

ts
import slikr from "@briklab/slikr";

// Send large binary data with progress
async function sendLargeBinary(client: ReturnType<typeof slikr>, data: Uint8Array) {
  const chunkSize = 16 * 1024; // 16KB chunks
  const totalChunks = Math.ceil(data.length / chunkSize);

  for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, data.length);
    const chunk = data.slice(start, end);

    await client.send("binary_chunk", {
      index: i,
      total: totalChunks,
      data: chunk
    });

    // Report progress
    console.log(`Sent chunk ${i + 1}/${totalChunks}`);
  }

  await client.send("binary_complete", { size: data.length });
}

// Receive and reassemble binary data
function receiveLargeBinary(client: ReturnType<typeof slikr>) {
  const chunks: Uint8Array[] = [];
  let expectedChunks = 0;

  client.on("binary_chunk", (chunk) => {
    chunks[chunk.index] = chunk.data;
    console.log(`Received chunk ${chunk.index + 1}/${chunk.total}`);

    if (chunks.length === chunk.total) {
      // All chunks received, reassemble
      const totalSize = chunks.reduce((sum, c) => sum + c.length, 0);
      const result = new Uint8Array(totalSize);
      let offset = 0;

      for (const c of chunks) {
        result.set(c, offset);
        offset += c.length;
      }

      console.log("Binary data reassembled, size:", totalSize);
    }
  });
}

Transport-Specific Features

ts
import slikr from "@briklab/slikr";

const client = slikr("https://example.com/wt"); // WebTransport URL

// Check transport type
console.log("Using transport:", client.status.type);

// WebTransport-specific features (when available)
if (client.status.type === "WebTransport") {
  // WebTransport supports datagrams for unreliable delivery
  // slikr handles this automatically, but you can check status
}

// WebSocket fallback features
if (client.status.type === "WebSocket") {
  // WebSocket provides reliable ordered delivery
}

Performance Optimization

ts
import slikr from "@briklab/slikr";

const client = slikr("wss://high-throughput.example.com")
  // Minimize keepalive for high-frequency apps
  .keepalive(true)
  .keepalive(30000) // 30s intervals

  // Aggressive retry for critical connections
  .retry(10)
  .retryDelay(500)
  .retryTimeout(2000);

// Batch messages to reduce overhead
class MessageBatcher {
  private messages: Array<{ event: string; data: any }> = [];
  private batchSize = 10;
  private flushInterval = 100; // ms

  constructor(private client: ReturnType<typeof slikr>) {
    setInterval(() => this.flush(), this.flushInterval);
  }

  add(event: string, data: any) {
    this.messages.push({ event, data });

    if (this.messages.length >= this.batchSize) {
      this.flush();
    }
  }

  private async flush() {
    if (this.messages.length === 0) return;

    const batch = [...this.messages];
    this.messages = [];

    await this.client.send("batch", batch);
  }
}

Error Recovery Patterns

ts
import slikr from "@briklab/slikr";
import { Slikr } from "@briklab/slikr";

class ResilientClient {
  private client: ReturnType<typeof slikr>;
  private reconnectTimer?: NodeJS.Timeout;

  constructor(url: string) {
    this.client = slikr(url)
      .retry(3)
      .totalTimeout(10000)
      .onTotalTimeout(() => this.scheduleReconnect());
  }

  private scheduleReconnect() {
    if (this.reconnectTimer) return;

    this.reconnectTimer = setTimeout(async () => {
      this.reconnectTimer = undefined;
      try {
        await this.client.connect();
        console.log("Reconnected successfully");
      } catch (error) {
        console.error("Reconnection failed, will retry later");
        this.scheduleReconnect(); // Exponential backoff could be added here
      }
    }, 5000);
  }

  async connect() {
    try {
      await this.client.connect();
    } catch (error) {
      if (error instanceof Slikr.Error) {
        console.error("Connection failed:", error.message);
        this.scheduleReconnect();
      }
      throw error;
    }
  }
}

Live Demo

Console
No logs yet.

Additional Resources