Skip to main content
Slung provides official client SDKs that handle binary protocol encoding, connection management, and error handling.

TypeScript/Node.js SDK

The TypeScript SDK provides a simple, type-safe client for Node.js applications.

Installation

cd sdks/client/typescript
pnpm install

Quick Start

import { SlungClient } from "@slunghq/client";

const client = new SlungClient("ws://127.0.0.1:2077");
await client.connect();

client.sendEvent({
  value: 23.5,
  timestamp: Date.now() * 1000, // Convert to microseconds
  series: "temp",
  tags: ["sensor=1", "env=prod"]
});

client.close();

API Reference

SlungClient

Main client class for connecting to Slung and sending events. Constructor
const client = new SlungClient(url?: string)
  • url: WebSocket URL (default: ws://127.0.0.1:2077)
Methods
// Connect to Slung server
await client.connect(): Promise<void>

// Send a single event
client.sendEvent(event: SlungEvent): void

// Start simulated event stream (for testing)
client.startSimulatedStream(options?: SimulatedStreamOptions): () => void

// Close connection
client.close(code?: number, reason?: string): void

SlungEvent

Event structure for metrics:
type SlungEvent = {
  value: number;        // Metric value
  timestamp: number;    // Unix microseconds
  series: string;       // Series name
  tags: string[];       // Tags (format: "key=value")
};

Timestamp Helpers

import { nowUnixMicros } from "@slunghq/client";

const timestamp = nowUnixMicros(); // Current time in microseconds

Real-World Examples

Streaming Simulated Data

From examples/simulated.ts:
import { SlungClient } from "../src/index.ts";

async function main(): Promise<void> {
  const client = new SlungClient(
    process.env.SLUNG_WS_URL ?? "ws://127.0.0.1:2077",
  );
  await client.connect();

  console.log("connected to slung websocket");
  console.log("sending simulated events every 10ms");

  const stop = client.startSimulatedStream({
    intervalMs: 10,
    initialValue: 90,
    jitter: 1.2,
    series: "temp",
    tags: ["sensor=1", "env=dev", "service=api"],
  });

  const shutdown = () => {
    stop();
    client.close(1000, "shutdown");
    process.exit(0);
  };

  process.on("SIGINT", shutdown);
  process.on("SIGTERM", shutdown);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
Run the example:
pnpm run example:simulated

Manual Event Sending

import { SlungClient, nowUnixMicros } from "@slunghq/client";

const client = new SlungClient();
await client.connect();

// Send CPU usage
client.sendEvent({
  value: 45.2,
  timestamp: nowUnixMicros(),
  series: "cpu.usage",
  tags: ["host=server-1", "core=0"]
});

// Send memory metrics
client.sendEvent({
  value: 8192.5,
  timestamp: nowUnixMicros(),
  series: "memory.used",
  tags: ["host=server-1", "unit=mb"]
});

High-Throughput Ingestion

import { SlungClient, nowUnixMicros } from "@slunghq/client";

const client = new SlungClient();
await client.connect();

// Send batch of events
const events = Array.from({ length: 1000 }, (_, i) => ({
  value: Math.random() * 100,
  timestamp: nowUnixMicros(),
  series: "metric.batch",
  tags: [`index=${i}`, "batch=true"]
}));

for (const event of events) {
  client.sendEvent(event);
}

Binary Encoding

The SDK handles binary encoding automatically via encodeEventBinary (from src/wire.ts):
export function encodeEventBinary(event: WireEvent): Buffer {
  const seriesBuf = toUtf8(event.series, "series");
  const tagBufs = event.tags.map((tag) => toUtf8(tag, "tag"));
  return encodeFromBuffers(event.timestamp, event.value, seriesBuf, tagBufs);
}

function encodeFromBuffers(
  timestamp: number,
  value: number,
  seriesBuf: Buffer,
  tagBufs: Buffer[],
): Buffer {
  const HEADER_BYTES = 8 + 8 + 2 + 2;
  const MAX_U16 = 0xffff;
  
  if (tagBufs.length > MAX_U16) {
    throw new Error(`tag count exceeds ${MAX_U16}`);
  }

  let total = HEADER_BYTES + seriesBuf.length;
  for (const tag of tagBufs) total += 2 + tag.length;

  const out = Buffer.allocUnsafe(total);
  let offset = 0;
  
  // Write header
  out.writeBigInt64LE(BigInt(Math.trunc(timestamp)), offset);
  offset += 8;
  out.writeDoubleLE(value, offset);
  offset += 8;
  out.writeUInt16LE(seriesBuf.length, offset);
  offset += 2;
  out.writeUInt16LE(tagBufs.length, offset);
  offset += 2;
  
  // Write series
  seriesBuf.copy(out, offset);
  offset += seriesBuf.length;

  // Write tags
  for (const tag of tagBufs) {
    out.writeUInt16LE(tag.length, offset);
    offset += 2;
    tag.copy(out, offset);
    offset += tag.length;
  }

  return out;
}
You can also import encoding functions directly:
import { encodeEventBinary, createEventBinaryEncoder } from "@slunghq/client";

// One-off encoding
const buffer = encodeEventBinary({
  timestamp: nowUnixMicros(),
  value: 42.0,
  series: "temp",
  tags: ["sensor=1"]
});

// Pre-compiled encoder (reuses buffers)
const encoder = createEventBinaryEncoder("temp", ["sensor=1"]);
const buffer = encoder(nowUnixMicros(), 42.0);

Benchmarking

Run ingestion benchmarks:
# Send 1M events in batches of 2000
pnpm run example:benchmark -- --count 1000000 --batch 2000 --series temp --tags sensor=1,env=bench

# Duration-based benchmark (30 seconds)
pnpm run example:benchmark -- --duration 30 --batch 2000 --series temp --tags sensor=1,env=bench --linger-ms 3000
Benchmark options:
--url ws://127.0.0.1:2077      # Server URL
--count 1000000                 # Total events to send
--duration 30                   # Run for N seconds
--batch 2000                    # Events per batch
--series temp                   # Series name
--tags sensor=1,env=bench       # Comma-separated tags
--max-buffered 4194304          # Max buffer size
--linger-ms 3000                # Linger time after sending
--progress-ms 1000              # Progress update interval

Error Handling

import { SlungClient } from "@slunghq/client";

const client = new SlungClient();

try {
  await client.connect();
  console.log("Connected successfully");
} catch (err) {
  console.error("Connection failed:", err);
  process.exit(1);
}

try {
  client.sendEvent({
    value: 23.5,
    timestamp: Date.now() * 1000,
    series: "temp",
    tags: ["sensor=1"]
  });
} catch (err) {
  console.error("Send failed:", err);
  // WebSocket not connected - call connect() first
}

Other SDKs

Additional language SDKs are planned. Contributions welcome!

Next Steps

WebSocket Protocol

Implement your own client using the protocol specification

Ingestion Overview

Learn about other ingestion methods

Build docs developers (and LLMs) love