JavaScript (server)

Checks Switchyard feature flags from server-side JavaScript: Node 20+, Deno, Bun and edge runtimes. No dependencies; it uses the standard fetch.

The client keeps the environment's flags in memory and evaluates them there, so checking a flag is a function call, not a network request. Changes arrive within about a second over a live-update stream, with polling as a safety net. If the server goes away, checks keep using the last flags received.

Use it in your app

ts
import { SwitchyardClient } from 'switchyard-js';

const flags = new SwitchyardClient({
  sdkKey: process.env.SWITCHYARD_SDK_KEY!, // the environment's SDK key
  baseUrl: 'https://flags.example.com',   // your Switchyard server
});

// Plain on/off
if (flags.isEnabled('maintenance-banner', false)) {
  showBanner();
}

// Targeting rules and percentage rollouts need to know who's asking
const user = { key: 'user-42', attributes: { email: 'dev@acme.com', country: 'IN', plan: 'pro' } };
if (flags.isEnabled('new-checkout', user, false)) {
  return renderNewCheckout();
}

Flags that serve more than on/off (text, numbers, JSON) have typed methods. Each returns the default for a flag of another type:

ts
const color = flags.stringVariation('checkout-button', user, 'green');
const limit = flags.numberVariation('upload-limit-mb', user, 10);
const plan = flags.jsonVariation<{ seats: number }>('plan-config', user, { seats: 5 });

Optional extras:

ts
await flags.ready(2000);               // wait up to 2 s for the first fetch (checks work before, with defaults)
flags.onChange((keys) => console.log('changed:', keys));
flags.status;                          // 'starting' | 'ready' | 'stale' | 'unauthorized' | 'closed'
flags.close();                         // when shutting down: the open stream keeps Node running otherwise

Options

Option Default
sdkKey required The environment's SDK key
baseUrl http://localhost:8080 The Switchyard server
pollInterval 30000 ms between checks while healthy (every 5 minutes while the stream is up)
requestTimeout 10000 ms per request
maxBackoff 300000 Longest wait between retries while the server is failing
streaming true Hold a live-update stream open
logger console Where warnings go; null to silence
fetch global fetch Your own, e.g. for a proxy

Not for browsers. The SDK key can read every flag's rules (including, say, the email addresses a rule lists), so keep it on your servers.

OpenFeature

switchyard-js/openfeature is an OpenFeature provider, for code written against the vendor-neutral OpenFeature API. Install @openfeature/server-sdk alongside:

ts
import { OpenFeature } from '@openfeature/server-sdk';
import { SwitchyardProvider } from 'switchyard-js/openfeature';

await OpenFeature.setProviderAndWait(new SwitchyardProvider({ sdkKey, baseUrl: 'https://flags.example.com' }));
const flags = OpenFeature.getClient();
const color = await flags.getStringValue('checkout-button', 'green', { targetingKey: userId, plan: 'pro' });

The targetingKey is the context key; other context values become attributes (numbers, booleans and dates as text). Reasons map to OpenFeature's: TARGETING_MATCH (a rule; its index is in the flag metadata), DEFAULT (everyone else), DISABLED (off), plus PREREQUISITE_FAILED. Unknown flags and the wrong type give your default with FLAG_NOT_FOUND or TYPE_MISMATCH. Flag changes are sent on as PROVIDER_CONFIGURATION_CHANGED events.

evaluateDetail gives the same reasons without OpenFeature.

Same answers as every other client

Evaluation follows spec/EVALUATION.md and passes the shared evaluation vectors, so a user gets the same result here as in the Java client.

Develop

bash
npm install
npm test           # node --test, including the shared vectors
npm run typecheck
npm run build      # ESM + type definitions in dist/
npm run bench      # how long a flag check takes

Node 24 runs the TypeScript sources directly, so the tests need no build step.