Server SDK
StatsKitServerClient runs on the server (Node, Bun, Deno, Cloudflare Workers, and other edge runtimes). It fetches the full compiled config once and evaluates every flag locally with the bundled engine, identical bucketing to the platform, no network per call, and full access to rules and segments.
Install
Section titled “Install”npm install @statskit/sdkImport from the /server entry point.
Initialize
Section titled “Initialize”import { StatsKitServerClient } from "@statskit/sdk/server";
export const statskit = await new StatsKitServerClient({ serverKey: process.env.VERSUCH_SERVER_KEY!, // ssk_live_… or vsk_live_…}).init();The client fetches the full payload on init() and holds it in memory. On Node 18 and earlier, pass a fetch implementation via the fetch option.
Evaluate, context per call
Section titled “Evaluate, context per call”Because the server holds the full config and evaluates locally, you pass the context per call. That’s what you want when one process serves many different users.
The context is { key, attributes }: key is the stable bucketing unit; attributes drive the targeting rules.
const ctx = { key: user.id, attributes: { plan: user.plan, country: user.country } };const showBeta = statskit.boolVariation("beta-dashboard", ctx, false);const variant = statskit.stringVariation("banner-theme", ctx, "default");const limit = statskit.numberVariation("upload-limit", ctx, 5);const layout = statskit.jsonVariation("home-layout", ctx, { hero: "a" });const cohort = statskit.variation<string>("cohort", ctx, "control");
// Value + reason + config versionconst { value, reason, version } = statskit.detail("beta-dashboard", ctx, false);Evaluation never throws, a missing flag or an outage returns the default you passed.
All flags at once
Section titled “All flags at once”allFlags(ctx) resolves every flag for a context in one pass, handy for a settings screen or for server-side rendering a bootstrap payload.
const flags = statskit.allFlags(ctx);Staying current
Section titled “Staying current”The server client detects publishes and hot-swaps the payload without a restart.
- Polling (default): it polls the cheap
GET /v1/config/versionand only re-fetches the full config when the version changes. Server default is every 30s; setpollIntervalMs(0 disables). - Streaming: set
stream: trueto subscribe to the SSE change stream at/v1/config/streamfor near-instant updates.
new StatsKitServerClient({ serverKey, stream: true }); // SSEnew StatsKitServerClient({ serverKey, pollIntervalMs: 10000 }); // faster pollServer-side rendering
Section titled “Server-side rendering”Evaluate on the server, inline the payload, and hand it to the browser client as bootstrap so the first paint matches the server with no network wait or flicker:
// serverconst client = await new StatsKitServerClient({ serverKey }).init();const flags = client.allFlags(ctx); // embed in HTML as window.__VERSUCH__
// browsernew StatsKitClient({ clientKey, bootstrap: window.__VERSUCH__ });Emitting metrics
Section titled “Emitting metrics”Emit custom-metric events from any backend with track(): the server-side companion to the tracker’s statskit.track():
// value is in MINOR units (cents); feeds "sum" / "mean" metricsawait statskit.track("user_123", "checkout", { value: 4999, currency: "usd" });
// conversion / count metrics need no valueawait statskit.track("user_123", "support_ticket");Custom cache
Section titled “Custom cache”Persist the payload anywhere by passing a CacheAdapter (memory is built in; a file cache ships under @statskit/sdk/node):
import type { CacheAdapter } from "@statskit/sdk";
const redisCache: CacheAdapter = { get: (k) => redis.get(k), set: (k, v) => redis.set(k, v),};
new StatsKitServerClient({ serverKey, cache: redisCache });For the full options table and error handling, see the SDK overview.