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