JavaScript / TypeScript
The @edgeflags/sdk package provides a type-safe, real-time interface for reading feature flags and configs from any JavaScript runtime (browsers, Node.js, Deno, Bun, edge workers).
SDKs are also available for React and Python.
The SDK uses the bulk evaluation endpoint under the hood — flags and configs are fetched in a single request and cached locally for synchronous reads.
Installation
npm install @edgeflags/sdkpnpm add @edgeflags/sdkyarn add @edgeflags/sdkQuick start
import { EdgeFlags } from '@edgeflags/sdk';
const ef = new EdgeFlags({ token: 'ff_production_abc123', baseUrl: 'https://edgeflags.net', context: { user_id: 'u_42', plan: 'premium', custom: { country: 'US' }, },});
await ef.init();
const darkMode = ef.flag('dark_mode', false);const limits = ef.config('api_limits', { requests_per_minute: 100 });After init() resolves, all reads are synchronous from the local cache. The SDK polls for updates in the background.
Configuration
Pass these options to the EdgeFlags constructor:
| Option | Type | Default | Description |
|---|---|---|---|
token | string | required | Bearer token for authentication |
baseUrl | string | required | EdgeFlags API URL |
context | EvaluationContext | undefined | User context for targeting evaluation |
pollingInterval | number | 60000 | Background poll interval in milliseconds |
bootstrap | object | undefined | Initial flag/config values for instant reads before init() |
debug | boolean | false | Log SDK activity to the console |
Evaluation context
The context object is sent with every evaluation request and drives targeting rules:
interface EvaluationContext { user_id?: string; email?: string; phone?: string; plan?: string; segments?: string[]; environment?: string; custom: Record<string, unknown>;}Reading flags
Use flag() to read a flag value. Pass a default as the second argument — its type narrows the return type.
// Returns boolean | string | number | object | undefinedconst raw = ef.flag('dark_mode');
// Returns boolean (default provides type + fallback)const darkMode = ef.flag('dark_mode', false);
// String flagsconst banner = ef.flag('banner_text', '');
// Number flagsconst limit = ef.flag('request_limit', 1000);
// JSON flagsconst theme = ef.flag('theme_override', { primary: '#000' });Use allFlags() to get every cached flag as a Record<string, FlagValue>.
Reading configs
Use config() the same way. The generic parameter provides type safety:
const providers = ef.config<PaymentConfig>('payment_providers', { stripe_enabled: true, paypal_enabled: false,});
const all = ef.allConfigs();Identifying users
Call identify() to update the evaluation context and immediately refresh all values:
await ef.identify({ user_id: 'u_99', email: 'jane@example.com', plan: 'enterprise', custom: { beta_enrolled: true },});
// Flags and configs now reflect the new userEvents
The SDK emits three events:
| Event | Payload | When |
|---|---|---|
ready | undefined | init() completes successfully |
change | ChangeEvent | Flag or config values change after a poll |
error | Error | Network or API error during polling |
// Subscribe — returns an unsubscribe functionconst off = ef.on('change', (event) => { for (const { key, previous, current } of event.flags) { console.log(`Flag ${key}: ${previous} → ${current}`); }});
ef.on('error', (err) => { console.error('EdgeFlags polling error:', err);});
// Unsubscribeoff();Bootstrap and offline fallback
Provide bootstrap values so flags are available immediately, before the first network request:
const ef = new EdgeFlags({ token: 'ff_production_abc123', baseUrl: 'https://edgeflags.net', bootstrap: { flags: { dark_mode: false, new_checkout: true }, configs: { api_limits: { requests_per_minute: 500 } }, },});
// Available immediately — no await neededconst darkMode = ef.flag('dark_mode', false);
// init() will replace bootstrap values with live dataawait ef.init();Streaming mode
By default the SDK polls for updates on a fixed interval. When the EdgeFlags server has WebSocket support configured, you can switch to streaming mode for sub-50ms push updates instead:
const ef = new EdgeFlags({ token: 'ff_production_abc123', baseUrl: 'https://edgeflags.net', context: { user_id: 'u_42', custom: {}, }, streaming: true,});
await ef.init();With streaming: true, the SDK opens a WebSocket to /stream/flags, subscribes with the current context, and applies diffs as they arrive. The change event fires on each diff.
See the Real-time Streaming guide for full WebSocket protocol details.
Testing
Use createMockClient() to create a client that returns static values without making network requests:
import { createMockClient } from '@edgeflags/sdk';
const mock = createMockClient({ flags: { dark_mode: true, new_checkout: false }, configs: { api_limits: { requests_per_minute: 9999 } },});
// Works like a real clientmock.flag('dark_mode', false); // truemock.config('api_limits'); // { requests_per_minute: 9999 }Cleanup
Call destroy() to stop polling and release resources:
ef.destroy();Next steps
- React SDK — hooks and context provider for React apps
- Python SDK — async and sync clients for Python 3.10+