Skip to content

SDK overview

@versuch/sdk is the official SDK for Versuch feature flags. It does local caching, local evaluation, exposure tracking, and has a plugin system, for browsers, Node, Bun, Deno, and edge runtimes (Cloudflare Workers and others).

Terminal window
npm install @versuch/sdk
ImportExportsUse in
@versuch/sdkVersuchClient, typesBrowser / public
@versuch/sdk/serverVersuchServerClientServer / edge
@versuch/sdk/reactVersuchProvider, useFlagReact apps
@versuch/sdk/nodefile cache adapterNode servers

Each client is paired with a key that is safe where it runs.

VersuchClient

Browser / mobile. Uses a public key (vpk_live_… or csk_live_…). Receives a pre-evaluated payload, no targeting rules. Browser SDK →

VersuchServerClient

Server / edge. Uses a secret key (vsk_live_… or ssk_live_…). Fetches the full config and evaluates locally with rules and segments. Server SDK →

The public key is safe in a browser bundle: the API only returns a sanitized, pre-evaluated payload to it. The clients guard the boundary, the server client throws in a browser, and the browser client throws if handed a secret key.

Available on both clients (the browser client takes no per-call context; the server client takes context per call).

MethodReturns
boolVariation(key, [ctx,] default)boolean
stringVariation(key, [ctx,] default)string
numberVariation(key, [ctx,] default)number
jsonVariation(key, [ctx,] default)object
variation<T>(key, [ctx,] default)typed T
allFlags([ctx])every flag resolved
detail(key, [ctx,] default)value + reason + version

Evaluation never throws, a missing flag or a network outage returns the default you passed.

A context is { key, attributes }:

  • key: the stable bucketing unit; deterministic assignment keys off it.
  • attributes: the fields targeting rules reference (plan, country, beta_user, …).

The browser client holds one context at a time (set at init, changed with identify()); the server client takes context per evaluation call. See flag concepts.

OptionDefaultNotes
environment"production"Which env’s config to read
baseUrlhttps://api.versuch.ai
cacheautoCacheAdapter, or false to disable persistence
pollIntervalMs60000 client / 30000 server0 disables
sendExposurestrueRecord exposures for analytics
fetchglobal fetchRequired on Node 18 and earlier
timeoutMs8000
maxRetries2
loggerofftrue for console, or a Logger
plugins[]
offlinefalseNever hit the network

The SDK caches the config payload so evaluation is instant on subsequent loads:

  • Browser: localStorage. A returning visitor evaluates on the first line, before any request completes; fresh config polls in the background.
  • Server: memory by default; pass a CacheAdapter to persist anywhere (a file cache ships under @versuch/sdk/node).
import type { CacheAdapter } from "@versuch/sdk";
const redisCache: CacheAdapter = {
get: (k) => redis.get(k),
set: (k, v) => redis.set(k, v),
};

Updates arrive via polling (client ~60s, server ~30s) or SSE (stream: true). Both use the cheap GET /v1/config/version poll to avoid re-fetching unchanged config.

Observe evaluations, config changes, and errors by passing plugins:

new VersuchClient({
clientKey,
plugins: [
{
name: "logger",
onEvaluation: (d) => console.log(d.flagKey, "", d.value),
onConfigChange: (c) => console.log("published v" + c.toVersion),
},
],
});

Other extension points: fetch (custom transport/proxy), logger, pollIntervalMs, timeoutMs, maxRetries, and offline (serve only cache/bootstrap, great for tests and SSR).

Every variation() records an exposure (which context saw which variation) and batches it for experiment analytics. Only the context key is sent, the server hashes it, attributes are never included. Browser exposures post to /v1/exposures; the server client batches and flushes. Disable with sendExposures: false.

All failures are a VersuchError with a stable .code:

UNAUTHORIZED, NOT_CONFIGURED, NETWORK, TIMEOUT, PARSE, CACHE, MISUSE, CONFIG.

import { VersuchError } from "@versuch/sdk";
try {
await client.init();
} catch (err) {
if (err instanceof VersuchError && err.code === "UNAUTHORIZED") {
// wrong key or environment
}
}

Evaluation itself never throws, only setup/transport paths do.