Skip to main content

JavaScript / TypeScript SDK

The Cuttlegate JS/TS SDK evaluates feature flags via the Cuttlegate HTTP API. It works in Node.js (v20+) and in the browser.

Installation

npm install @cuttlegate/sdk

Quick start

import { createClient } from '@cuttlegate/sdk';

const cg = createClient({
baseUrl: 'http://localhost:8080',
token: 'cg_YOUR_API_KEY',
project: 'my-project',
environment: 'production',
});

// Evaluate a single flag
const result = await cg.evaluate('dark-mode', {
user_id: 'user-123',
attributes: { plan: 'pro' },
});

console.log(result.enabled); // true or false
console.log(result.variant); // "true", "false", or a variant key
console.log(result.reason); // "disabled", "default", "rule_match", etc.

Configuration

createClient(config) accepts:

OptionTypeRequiredDescription
baseUrlstringYesCuttlegate server URL (e.g. http://localhost:8080)
tokenstringYesAPI key (cg_...) or OIDC Bearer token
projectstringYesProject slug
environmentstringYesEnvironment slug to evaluate against
timeoutnumberNoRequest timeout in ms (default: 5000)
fetchtypeof fetchNoCustom fetch implementation

Evaluating flags

Single flag

const result = await cg.evaluate('my-flag', {
user_id: 'user-123',
attributes: { plan: 'pro', country: 'DE' },
});
// { key, enabled, variant, reason, evaluatedAt }

If the flag does not exist, the SDK throws CuttlegateError with code: 'not_found'.

Convenience methods

const enabled = await cg.bool('dark-mode', ctx);   // returns boolean
const variant = await cg.string('banner-text', ctx); // returns variant string

Bulk evaluation

const results = await cg.evaluateAll({
user_id: 'user-123',
attributes: { plan: 'pro' },
});
// EvalResult[] — one entry per flag in the project/environment

Error handling

The SDK throws CuttlegateError with a machine-readable code:

import { CuttlegateError } from '@cuttlegate/sdk';

try {
await cg.evaluate('my-flag', ctx);
} catch (err) {
if (err instanceof CuttlegateError) {
switch (err.code) {
case 'unauthorized': // invalid or expired token
case 'forbidden': // token lacks access to this project/environment
case 'not_found': // flag key does not exist
case 'timeout': // request exceeded timeout
case 'network_error': // server unreachable or non-2xx response
case 'invalid_response': // response didn't match expected schema
}
}
}

CachedClient

For hot paths (e.g. React components, request handlers), use createCachedClient. It seeds an in-memory cache via HTTP, keeps it fresh via SSE, and serves reads from cache with zero network calls:

import { createCachedClient } from '@cuttlegate/sdk';

const client = createCachedClient(
{
baseUrl: 'http://localhost:8080',
token: 'cg_YOUR_API_KEY',
project: 'my-project',
environment: 'production',
},
{ context: { user_id: 'user-123', attributes: { plan: 'pro' } } },
);

await client.ready; // wait for initial hydration

const enabled = await client.bool('dark-mode', ctx); // cache hit — no HTTP call
client.close(); // stop SSE connection

Offline persistence (FlagStore)

By default, the cache lives only in memory. If the server is unreachable during hydration, ready rejects. To survive page reloads or process restarts, provide a FlagStore:

import type { FlagStore, FlagStoreEntry } from '@cuttlegate/sdk';

const localStorageStore: FlagStore = {
async save(flags: FlagStoreEntry[]) {
localStorage.setItem('cg-flags', JSON.stringify(flags));
},
async load(): Promise<FlagStoreEntry[]> {
const raw = localStorage.getItem('cg-flags');
return raw ? JSON.parse(raw) : [];
},
};

const client = createCachedClient(config, {
store: localStorageStore,
});

When store is set:

  • save is called after successful hydration and on every SSE update.
  • load is called when hydration fails (server unreachable, timeout, 5xx). If it returns a non-empty array, the cache is seeded from it and ready resolves.
  • Auth errors (401/403) never fall back to the store.

The SDK ships with noopFlagStore (the default — no persistence).

Testing

See Testing for the in-process mock client that lets you test flag-dependent code without a running server.

Gotchas

See Gotchas & Known Behaviours for non-obvious runtime behaviours you may encounter when using the SDK.