flagwithme-sdk
v1.1.0
Published
Official Node.js SDK for FlagWithMe - A powerful feature flag management system with support for boolean, string, number, JSON, and multivariate flags
Maintainers
Readme
FlagWithMe SDK
Official Node.js SDK for FlagWithMe — a self-hosted feature flag management system.
Features
- ✅ 5 Flag Types: Boolean, String, Number, JSON, Multivariate (A/B/C testing)
- ✅ Server-Side Evaluation: Rollout %, segment targeting, and rules evaluated on the backend
- ✅ Per-User Targeting: Pass user context at evaluation time — no shared mutable state
- ✅ Per-User Cache: Each unique user context cached independently with configurable TTL
- ✅ TypeScript Support: Full type definitions included
- ✅ Lightweight: Only 1 dependency (axios)
Installation
npm install flagwithme-sdkQuick Start
1. Get your SDK key
In the FlagWithMe dashboard: Project → Environments → copy the SDK Key for your environment.
2. Initialize the client
const FlagWithMeClient = require('flagwithme-sdk');
// Only two fields required — the SDK key identifies the project and environment
const client = new FlagWithMeClient({
apiUrl: 'http://localhost:4001/api/v1',
sdkKey: 'fwm_sdk_xxxxxxxxxxxx',
});3. Evaluate flags
// Global flag — on/off for everyone, no user context needed
const isMaintenance = await client.isEnabled('maintenance-mode');
// Per-user flag — pass user context at evaluation time
const user = { id: req.user.id, email: req.user.email };
const showNewUI = await client.isEnabled('new-dashboard', false, user);
const apiEndpoint = await client.getString('api-endpoint', 'https://default.com', user);
const uploadLimit = await client.getNumber('max-upload-mb', 10, user);
const themeConfig = await client.getJSON('theme-config', {}, user);
const buttonVariant = await client.getVariant('button-test', 'control', user);Configuration
Constructor options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiUrl | string | ✅ Yes | Base URL of the FlagWithMe API |
| sdkKey | string | ✅ Yes | Environment SDK key — identifies and authenticates the environment |
| environment | string | No | Environment name — informational only (sdkKey already identifies it) |
| cacheDuration | number | No | Cache TTL per user context in ms (default: 60000) |
| logger | object | No | Custom logger with error/warn/debug methods. Set FLAGWITHME_DEBUG=true to use console. |
| apiKey | string | No | Reserved for future use |
| projectId | string | No | Reserved for future use |
Environment variables (recommended)
FLAG_API_URL=https://your-backend.com/api/v1
FLAG_SDK_KEY=fwm_sdk_xxxxxxxxxxxxconst client = new FlagWithMeClient({
apiUrl: process.env.FLAG_API_URL,
sdkKey: process.env.FLAG_SDK_KEY,
});API Reference
isEnabled(flagKey, defaultValue?, user?)
Evaluate a boolean feature flag.
// Global
const isOn = await client.isEnabled('maintenance-mode');
// Per-user
const showBeta = await client.isEnabled('beta-feature', false, { id: 'user-123' });| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| flagKey | string | — | Flag key from the dashboard |
| defaultValue | boolean | false | Returned when flag not found or an error occurs |
| user | UserContext | — | User context for rollout and segment targeting |
Returns: Promise<boolean>
getString(flagKey, defaultValue?, user?)
Get a string flag value.
const message = await client.getString('welcome-message', 'Hello!', { id: 'user-123' });Returns: Promise<string>
getNumber(flagKey, defaultValue?, user?)
Get a number flag value.
const timeout = await client.getNumber('api-timeout', 5000, { id: 'user-123' });Returns: Promise<number>
getJSON(flagKey, defaultValue?, user?)
Get a JSON object flag value.
const config = await client.getJSON('app-config', { theme: 'light' }, { id: 'user-123' });Returns: Promise<Object>
getVariant(flagKey, defaultVariant?, user?)
Get a multivariate flag variant. Variant selection and weight distribution is handled server-side.
const variant = await client.getVariant('checkout-flow', 'control', { id: 'user-123' });
switch (variant) {
case 'one-page': return renderOnePageCheckout();
case 'multi-step': return renderMultiStepCheckout();
default: return renderDefaultCheckout();
}Returns: Promise<string>
getAllFlags(user?)
Get all flags with their resolved values for a given user context.
const flags = await client.getAllFlags({ id: 'user-123' });
// { 'new-dashboard': true, 'api-url': 'https://...', 'timeout': 5000 }Returns: Promise<Object>
setUser(user)
Set a default user context used when no user is passed to evaluation methods.
Useful for single-user environments (mobile apps, CLIs).
For web servers, prefer passing user context per evaluation call.
client.setUser({ id: 'user-123', email: '[email protected]', plan: 'premium' });
// Now evaluation calls without a user param use this context
const isOn = await client.isEnabled('feature');
client.setUser(null); // clear defaultclearCache()
Clear the entire flag cache. Forces a fresh evaluation on next call.
client.clearCache();setEnvironment(environment, sdkKey?)
Switch to a different environment. Always clears the cache.
client.setEnvironment('staging', 'fwm_sdk_staging_xxxxxxxxxxxx');login(email, password)
Authenticate against the admin API. Not required for flag evaluation — the SDK key handles that. Useful for admin operations.
const token = await client.login('[email protected]', 'password');User Context
The user parameter tells the backend who is making this request so it can apply targeting rules configured in the dashboard.
// Minimum — enables rollout bucketing
{ id: 'user-123' }
// With email — enables email-based segment rules
{ id: 'user-123', email: '[email protected]' }
// With custom attributes — enables attribute-based segment rules
{ id: 'user-123', email: '[email protected]', plan: 'premium', country: 'US' }Custom attributes are matched against segment rules you define in the dashboard (e.g. plan equals 'premium').
If no user context is provided, the backend evaluates the flag without targeting — the result applies to all users equally.
Usage Patterns
Express.js (recommended pattern)
const FlagWithMeClient = require('flagwithme-sdk');
// Initialize once at app startup
const flagClient = new FlagWithMeClient({
apiUrl: process.env.FLAG_API_URL,
sdkKey: process.env.FLAG_SDK_KEY,
});
// Per request — pass user context at evaluation time
app.get('/dashboard', async (req, res) => {
const user = { id: req.user.id, email: req.user.email };
const showNewUI = await flagClient.isEnabled('new-dashboard', false, user);
const config = await flagClient.getJSON('app-config', {}, user);
res.render('dashboard', { showNewUI, config });
});Webhook-triggered cache refresh
// When a flag changes in the dashboard, the backend can notify your server
app.post('/webhooks/flags', (req, res) => {
flagClient.clearCache();
res.sendStatus(200);
});Kill switch
const isAvailable = await client.isEnabled('payment-service', true);
if (!isAvailable) {
return res.status(503).json({ error: 'Service temporarily unavailable' });
}Remote configuration
const limits = await client.getJSON('upload-limits', { maxSizeMb: 10, allowedTypes: ['jpg', 'png'] });
upload.limits = { fileSize: limits.maxSizeMb * 1024 * 1024 };TypeScript Support
import FlagWithMeClient, { FlagWithMeConfig, UserContext } from 'flagwithme-sdk';
const config: FlagWithMeConfig = {
apiUrl: 'http://localhost:4001/api/v1',
sdkKey: 'fwm_sdk_xxxxxxxxxxxx',
};
const client = new FlagWithMeClient(config);
const user: UserContext = { id: 'user-123', email: '[email protected]' };
const enabled: boolean = await client.isEnabled('feature', false, user);
const message: string = await client.getString('message', '', user);
const timeout: number = await client.getNumber('timeout', 5000, user);Caching
Flags are cached per user context with a configurable TTL (default: 60 seconds).
- Each unique user context gets its own cache entry
- Cache holds up to 500 user contexts — oldest entry is evicted when full
- Cache is invalidated automatically when you call
clearCache()orsetEnvironment() - Stale cache is served as a fallback if the backend is temporarily unreachable
// Reduce TTL if you need near-real-time flag updates
const client = new FlagWithMeClient({
apiUrl: process.env.FLAG_API_URL,
sdkKey: process.env.FLAG_SDK_KEY,
cacheDuration: 10000, // 10 seconds
});Troubleshooting
Flags always return default values
- Check the flag is enabled in the environment (dashboard → Flags → toggle)
- Check rollout percentage is > 0%
- Verify
sdkKeymatches the environment you expect - Enable debug logging:
FLAGWITHME_DEBUG=true node app.js
Segment rules not matching
Check that the attributes you pass in user match the rule field names in the dashboard exactly (case-sensitive). For example, if the rule says plan equals premium, pass { plan: 'premium' }.
Backend unreachable
The SDK returns the stale cache if available, or the caller's defaultValue if the cache is empty. Set FLAGWITHME_DEBUG=true to see the error logged.
Requirements
- Node.js 14.0.0 or higher
- FlagWithMe backend running and accessible
License
MIT
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Version: 1.2.0 | Made with ❤️ by Thamizhmani C
