@flagoff/ts-sdk
v1.2.0
Published
FlagOff - A comprehensive dual-purpose JavaScript SDK for feature flags with client-side and server-side support
Readme
Flagoff TypeScript SDK
[!IMPORTANT] Private Early Access: Flagoff is currently in private early access. You will need an API key to use this SDK. Please email [email protected] to request access.
Flagoff is a resilient, privacy-focused feature flagging SDK for TypeScript applications. Built for modern development, it offers:
- Offline-First Architecture: Your app keeps working even when the network doesn't.
- Real-Time Updates: Toggle flags instantly via WebSockets without page reloads.
- Privacy by Design: Flags are evaluated locally on the device, keeping user data secure.
- Universal Support: Works seamlessly in React, Node.js, and vanilla JavaScript.
Table of Contents
- Quick Start
- Key Concepts
- Framework Guides
- API Reference
- Configuration
- Persistent Traits & Targeting
- User Identification & Privacy
Quick Start
1. Installation
npm install @flagoff/ts-sdk
# or
yarn add @flagoff/ts-sdk2. Basic Usage
Initialise the client and evaluate a flag.
import { FeatureFlagsClient } from '@flagoff/ts-sdk';
const client = new FeatureFlagsClient({
apiKey: 'YOUR_API_KEY',
offlineMode: 'cache-only', // Recommended for resilience
});
async function main() {
// 1. Initialise (connects, caches flags, starts auto-recovery)
await client.initialize();
// 2. Evaluate a flag
const flag = await client.evaluateFlag('new-feature', 'user-123');
if (flag.enabled) {
console.log('New feature is enabled!');
}
}
main();3. User Identity Management
Centralised user context management with the identity() method. When you switch users, all flags automatically re-evaluate.
import { FeatureFlagsClient } from '@flagoff/ts-sdk';
const client = new FeatureFlagsClient({ apiKey: 'YOUR_KEY' });
await client.initialize();
// Set user identity
await client.identity('user-123', { plan: 'pro', region: 'us-east' });
// Evaluate flags (uses current identity automatically)
const flag = await client.evaluateFlag('new-feature');
// Switch to a different user (all flags re-evaluate automatically)
await client.identity('user-456', { plan: 'free' });
// Clear identity and go anonymous
await client.anonymous();Benefits:
- ✅ No manual flag iteration needed
- ✅ WebSocket updates use current identity context
- ✅ Identity persists across page reloads
- ✅ Automatic re-evaluation on user switch
Key Concepts
🛡️ Offline-First & Auto-Recovery
Flagoff is designed to be indestructible.
- Cache-Only Mode: If the SDK cannot connect to the server on startup, it instantly falls back to the last known good configuration from local storage. Your app loads without waiting for a timeout.
- Auto-Recovery: In the background, the SDK intelligently polls with exponential backoff until the connection is restored, then silently updates the flags.
- Smart Retries: The SDK automatically stops retrying if it encounters a terminal error (like a 401 Unauthorized or 404 Not Found), preventing wasteful network requests.
- Recovery Events: You can subscribe to
recoveryRetryandrecoveryFailedevents to show connection status to your users. You can view the events in the client offline-demo.ts and server offline-demo.ts.
⚡ Local Evaluation & Hashing
Unlike other SDKs that make a network request for every flag check, Flagoff evaluates flags locally.
- Deterministic Hashing: We use SHA-256 hashing to deterministically map users to flag variants. This ensures consistent experiences across sessions without needing a central server for every decision.
- Zero Latency: Flag evaluations happen in microseconds because they are just local math operations.
- Privacy: Your user's ID and traits never leave the device during evaluation.
🔄 Real-Time Updates
Stop asking users to refresh. Flagoff maintains a lightweight WebSocket connection to push flag changes instantly.
- Instant Rollouts: Enable a feature, and it appears on all connected clients immediately.
- Kill Switches: Disable a buggy feature globally in milliseconds.
Framework Guides
React
Wrap your app in the FeatureFlagsProvider and use the useFeatureFlag hook.
// App.tsx
import { FeatureFlagsProvider } from '@flagoff/ts-sdk/react';
const config = { apiKey: 'YOUR_KEY' };
export default function App() {
return (
<FeatureFlagsProvider config={config}>
<MyComponent />
</FeatureFlagsProvider>
);
}// MyComponent.tsx
import { useFeatureFlag } from '@flagoff/ts-sdk/react';
export function MyComponent() {
const { enabled, value } = useFeatureFlag('dark-mode', {
identifier: 'user-123',
});
if (enabled) {
return <div className="dark">Dark Mode Active: {value}</div>;
}
return <div>Light Mode</div>;
}Node.js (Server-Side)
The server SDK shares the same API but is optimized for Node.js environments (using in-memory storage by default).
import { FeatureFlagsServer } from '@flagoff/ts-sdk/server';
const client = new FeatureFlagsServer({ apiKey: 'SERVER_KEY' });
await client.initialize();
const flag = await client.evaluateFlag('api-rate-limit', 'tenant-456');import { FeatureFlagsServer } from '@flagoff/ts-sdk/server';
const client = new FeatureFlagsServer({ apiKey: 'SERVER_KEY' });
await client.initialize();
// Set identity for long-running processes or tenant-scoped services
await client.identity('tenant-456', { plan: 'enterprise', region: 'eu-west' });
// Evaluate flags using current identity
const flag = await client.evaluateFlag('api-rate-limit');
// Or override for specific evaluation
const flag2 = await client.evaluateFlag('api-rate-limit', 'user-789');API Reference
client.initialize()
Connects to the Flagoff network, loads flags into cache, and sets up real-time listeners. Always await this before evaluating flags.
client.identity(userId, traits?)
Set the user identity and automatically re-evaluate all flags. This centralizes user context management.
userId: Unique user identifier (e.g.,'user-123')traits: Optional object for user attributes. These persist and are merged with global config traits (see Persistent Traits & Targeting section).
await client.identity('user-123', { plan: 'pro' });client.getIdentity()
Get the current user identity.
const identity = client.getIdentity();
// { userId: 'user-123', traits: { plan: 'pro' } }client.anonymous()
Clear the current identity and re-evaluate flags as anonymous.
await client.anonymous();client.evaluateFlag(key, identifier?, traits?)
Evaluates a flag for a specific user.
key: The flag identifier (e.g.,'new-checkout')identifier: User ID (optional if identity is set viaclient.identity())traits: Optional object for targeting rules (e.g.,{ plan: 'premium' })
// With identity set
await client.identity('user-123');
const flag = await client.evaluateFlag('new-feature'); // Uses current identity
// Override identity for specific evaluation
const flag2 = await client.evaluateFlag('new-feature', 'user-456', {
plan: 'free',
});client.on(event, callback)
Listen to lifecycle events.
'connected': SDK is online'flagsUpdated': New flags have arrived (via initial load or WebSocket)'identityChanged': User identity has changed
Configuration
| Option | Default | Description |
| :---------------- | :------------- | :------------------------------------------------------------------------------------------------------ |
| apiKey | Required | Your project API key. |
| offlineMode | 'cache-only' | Behavior when network fails on init. 'cache-only' loads from local storage; 'fail' throws an error. |
| enableCaching | true | Persist flags to localStorage (browser) or memory (server). |
| enableWebSocket | true | Receive real-time updates. |
| pollingInterval | 60000 | Base interval (ms) for auto-recovery polling. |
| traits | {} | Global user traits applied to every evaluation. Useful for app version, environment, etc. |
🎯 Persistent Traits & Targeting
Flagoff allows you to define user traits at multiple levels. When evaluating a flag, traits are merged in the following order of precedence:
- Evaluation Arguments (Highest Priority)
- User Identity (
client.identity()) - Global Configuration (
config.traits)
1. Global Configuration
Set traits that apply to every user (e.g., environment, app version).
const client = new FeatureFlagsClient({
apiKey: 'YOUR_KEY',
traits: {
appVersion: '1.2.0',
platform: 'ios',
},
});2. User Identity
Set traits for the current logged-in user. These stick until the user changes or logs out.
await client.identity('user-123', {
plan: 'premium',
country: 'en-GB',
});
// Automatically checks: environment='production' AND plan='premium' AND country='en-GB'
const flag = await client.evaluateFlag('premium-feature');3. Evaluation Override
Override everything for a specific check.
// Overrides 'plan' to 'free' just for this check
// Identifier is optional if identity is set via `client.identity()` and is safe to be undefined
const flag = await client.evaluateFlag('premium-feature', undefined, {
plan: 'free',
});🆔 User Identification & Privacy
Flagoff is designed to be privacy-first. Your user's ID and traits never leave the device during flag evaluation.
Identifier Precedence
When evaluating a flag, the SDK determines the userId using the following order of precedence:
- Provided Identifier: Passed directly to
evaluateFlag(key, 'user-123'). - Current Identity: Set via
client.identity('user-123'). - Default User ID: Configured via
defaultUserIdin options. - Anonymous: Falls back to
'anonymous'if nothing else is provided.
Privacy & Targeting Logic
Targeting rules (traits) are defined in the Flagoff dashboard but evaluated locally on the client.
- Definition: You define a rule (e.g.,
planEQUALSpremium). - Sync: The SDK downloads the targeting rules (not user lists).
- Evaluation: The SDK checks your local user traits against these rules in memory.
- Security: User traits (e.g., email, plan, country) are never sent to Flagoff servers.
