SDK

JavaScript / TypeScript

The official ShipSilently SDK for Node.js, Bun, Deno, Cloudflare Workers, and the browser. Ships as ESM and CJS with full TypeScript types.

Install

npm install @shipsilently/node
bun add @shipsilently/node
yarn add @shipsilently/node
pnpm add @shipsilently/node

Initialize the client

Create one client at app startup and reuse it for the lifetime of the process.

import { ShipSilentlyClient } from '@shipsilently/node';

export const flags = new ShipSilentlyClient({
  apiKey: process.env.SHIPSILENTLY_KEY!,
  // apiUrl defaults to https://shipsilently-cf.workers.dev, override to pin a region or self-host
});
const { ShipSilentlyClient } = require('@shipsilently/node');

const flags = new ShipSilentlyClient({
  apiKey: process.env.SHIPSILENTLY_KEY,
});

module.exports = { flags };

Configuration options

  • apiKey: required. Server keys start with sk_live_, client keys with pk_live_.
  • apiUrl: override the API endpoint. Defaults to https://shipsilently-cf.workers.dev.
  • fetch: inject a custom fetch implementation (useful in test environments).

Evaluate a single flag

Use evaluate() for a one-off async lookup. The third argument is the default value returned on network errors or unknown flags, types are inferred from it.

checkout.ts
import { flags } from './flags';

const useNewFlow = await flags.evaluate('new-checkout-v2', {
  userId: user.id,
  plan: user.plan,
  country: user.country,
}, false);

const heroVariant = await flags.evaluate('hero-variant', {
  userId: user.id,
}, 'control');  // returns 'control' | 'a' | 'b'

Evaluate all flags at once

evaluateAll() returns every flag in one round trip, ideal at request boundaries. Pair it with get() for synchronous lookups afterwards.

request.ts
await flags.evaluateAll({ userId: user.id, plan: user.plan });

// Synchronous reads, <1ms, hits the in-memory cache
const showBanner = flags.get('promo-banner', false);
const tier       = flags.get('pricing-tier', 'standard');

Stream flag updates

For long-running services, subscribe to a Server-Sent Events stream so flag changes propagate without polling. The client auto-reconnects with exponential backoff.

worker.ts
const stop = flags.stream(
  { userId: 'system' },
  (updated) => {
    console.log('flags refreshed:', Object.keys(updated));
  },
);

// later, on shutdown
stop();

Types

The SDK ships with full TypeScript types. The most common shapes:

types.ts
export type FlagValue = boolean | string | number | Record<string, unknown>;

export interface UserContext {
  userId?: string;
  email?:  string;
  country?: string;
  plan?:   string;
  [key: string]: string | number | boolean | undefined;
}

export interface EvaluationResult {
  flagKey: string;
  value:   FlagValue;
  reason:  'default' | 'rule_match' | 'rollout' | 'flag_disabled' | 'flag_not_found';
  ruleId?: string;
}

Framework integrations

The SDK has zero framework dependencies and runs anywhere standard fetch is available:

  • Next.js: initialize once in a server-only module; call evaluateAll() in route handlers or RSCs.
  • Cloudflare Workers: instantiate per-request with the env.SHIPSILENTLY_KEY binding.
  • Bun / Deno: works natively, no polyfills needed.
  • Browser: use a public client key (pk_live_…); the client never exposes secrets.
Heads up: Never ship server keys (sk_live_…) to the browser. Server keys can edit flag definitions; client keys can only evaluate.