FlagrshipGuidesReference
esc

JavaScript SDK

Fetch once, poll quietly, evaluate in memory. 2.1 KB gzipped, zero dependencies, Node 18 and later.

Install

$ npm install @flagrship/sdk

Ships CommonJS and ESM builds with TypeScript types. Works in Node, Bun, and Deno.

Usage

app.ts
1import { Flagrship } from '@flagrship/sdk';
2 
3const flags = new Flagrship({
4 apiKey: process.env.FLAGRSHIP_KEY,
5 defaults: { 'new-checkout': false },
6});
7 
8// Resolves after the first sync attempt, success or failure. Never rejects.
9await flags.ready();
10 
11if (flags.isEnabled('new-checkout', user.id)) {
12 showNewCheckout();
13}

Constructor options

OptionTypeDefaultWhat it does
apiKeystringrequiredA read-scoped key for one environment.
apiUrlstringhttps://api.flagrship.devOverride for self-hosted or local APIs.
pollIntervalnumber30000Milliseconds between polls. 0 disables polling entirely.
defaultsRecord<string, boolean>{}What isEnabled returns for flags the SDK has no config for.
onUpdate(flags) => void—Called after every successful config change, with a snapshot Map.
onError(error) => void—Called when a sync fails. The SDK keeps serving its last config regardless.
fetchtypeof fetchglobalThis.fetchInjectable for tests and non-standard runtimes.

Methods

ready()

Returns a promise that resolves after the first sync attempt. It never rejects. Await it once at startup if you want the first evaluation to reflect real config rather than defaults. If the API is unreachable, it still resolves, and onError is called.

isEnabled(flagKey, userId?, defaultValue?)

Synchronous, in-memory. The evaluation order:

  • Unknown flag → defaultValue, then options.defaults[flagKey], then false
  • Disabled → false
  • Rollout 100 → true, no user ID needed
  • No user ID and rollout below 100 → false (cannot bucket without an identifier)
  • Otherwise bucket(flagKey, userId) < rolloutPercentage

getFlag(flagKey)

The raw config object for a flag, or undefined. Useful for debugging and for showing rollout state in an admin UI.

{
"key": "new-checkout",
"enabled": true,
"rolloutPercentage": 25,
"targetingRules": null
}

allFlags()

A snapshot Map of every flag the SDK currently knows about. Safe to iterate while polling continues.

refresh()

Sync now instead of waiting for the next poll. Resolves true if config changed, false on a 304 or a failure. Never rejects.

close()

Stop polling. The last config stays in memory and isEnabled keeps working.

Sync behaviour

The SDK sends If-None-Match with the last ETag on every poll. A 304 means nothing changed and the in-memory Map is left alone; onUpdate does not fire. A 200 replaces the whole Map in one assignment, so a concurrent isEnabled call sees either the old config or the new one, never a mix.

The polling timer is unref()'d. A script that creates an SDK instance and finishes its work exits normally; a long-running server keeps polling for as long as it is alive for other reasons.

Bucketing

Two helpers are exported so you can reproduce the SDK's decision anywhere, for example to show a user which cohort they are in.

import { bucket, murmurhash3_x86_32 } from '@flagrship/sdk';
 
bucket('new-checkout', 'alice'); // 40
murmurhash3_x86_32('hello'); // 613153351
Flagrship · Ship without a release.