Official Node.js SDK for SetBit - Simple feature flags and A/B testing for server-side JavaScript.
- β Boolean Flags - Simple on/off feature toggles
- π§ͺ A/B Testing - Weighted variant distribution for experiments
- π Conversion Tracking - Track events and conversions
- π·οΈ Tag-Based Targeting - Target by environment, app, team, region, etc.
- π Fail-Open Design - Returns defaults if API is unreachable
- πͺΆ Zero Dependencies - Uses only Node.js built-in modules
- β‘ Fast - Built-in timeouts and connection handling
- π TypeScript Support - Full TypeScript definitions included
npm install @setbit-io/nodeOr with Yarn:
yarn add @setbit-io/nodeconst SetBitClient = require('@setbit-io/node');
// Initialize the client
const client = new SetBitClient({
apiKey: 'pk_your_api_key_here',
tags: {
env: 'production',
app: 'api',
}
});
// Check a boolean flag (user ID required for analytics)
const userId = getCurrentUserId();
const enabled = await client.enabled('new-checkout', userId, false);
if (enabled) {
showNewCheckout();
} else {
showOldCheckout();
}
// Get A/B test variant
const variant = await client.variant('pricing-experiment', userId, 'control');
switch (variant) {
case 'variant_a':
showPrice99();
break;
case 'variant_b':
showPrice149();
break;
default:
showPrice129();
}
// Track conversions (pass variant for proper attribution)
await client.track('purchase', userId, {
flagName: 'pricing-experiment',
variant: variant,
metadata: { amount: 99.99, currency: 'USD' }
});import SetBitClient, { SetBitConfig } from '@setbit-io/node';
const config: SetBitConfig = {
apiKey: 'pk_your_api_key',
tags: {
env: 'production',
app: 'api'
}
};
const client = new SetBitClient(config);
// All methods are fully typed
const enabled: boolean = await client.enabled('feature', userId, false);
const variant: string = await client.variant('experiment', userId, 'control');
await client.track('event', userId, { flagName: 'flag', variant, metadata: { key: 'value' } });const client = new SetBitClient({
apiKey: 'pk_your_api_key',
tags: {
env: 'production',
app: 'api',
region: 'us-east'
},
baseURL: 'https://flags.setbit.io', // Optional, custom endpoint
timeout: 3000, // Optional, request timeout in ms
silent: true // Optional, suppress error logs
});Config Parameters:
apiKey(string, required): Your SetBit API keytags(object, optional): Tags for targeting flagsbaseURL(string, optional): API endpoint URL (useful for self-hosted instances)timeout(number, optional): Request timeout in milliseconds (default: 3000)silent(boolean, optional): Suppress error logs (default: true)
Returns:
SetBitClient: SetBit client instance
Throws:
Error: If API key is missing
Check if a flag is enabled. Returns true if the flag is globally enabled, false otherwise.
Parameters:
flagName(string): Name of the flaguserId(string, required): User identifier (required for analytics and billing)defaultValue(boolean): Value to return if flag not found (default behavior on error)
Returns: Promise<boolean> - true if enabled, false otherwise
Note: This method returns whether the flag is globally enabled. For rollout flags, use variant() to check which rollout group the user is in.
Example:
// Check if flag is enabled
if (await client.enabled('new-feature', userId, false)) {
// Show new feature
}
// With default true (fail open)
if (await client.enabled('beta-access', userId, true)) {
// Grant beta access
}Get the variant for an A/B test experiment or percentage rollout.
Parameters:
flagName(string): Name of the experiment flaguserId(string, required): User identifierdefaultVariant(string): Default variant if flag not found (e.g., "control")
Returns: Promise<string> - Variant name (e.g., "control", "variant_a", "variant_b")
Note: Returns the defaultVariant if the flag is disabled or API is unreachable.
Example:
// A/B test
const variant = await client.variant('pricing-experiment', userId, 'control');
switch (variant) {
case 'variant_a':
// Show variant A
break;
case 'variant_b':
// Show variant B
break;
default:
// Show control
}
// Percentage rollout
const variant = await client.variant('new-api-rollout', userId, 'control');
if (variant === 'enabled') {
useNewAPI();
} else {
useOldAPI();
}Track a conversion event or user action.
Parameters:
eventName(string): Name of the event (e.g., "purchase", "signup", "click")userId(string, required): User identifieroptions(object, optional): Tracking optionsflagName(string): Flag name to associate with eventvariant(string): Variant the user was assigned to (for A/B test attribution)metadata(object): Additional metadata
Returns: Promise<void> - Resolves when tracking completes (fails silently)
Example:
// Track conversion with variant attribution (recommended for A/B tests)
const variant = await client.variant('pricing-experiment', userId, 'control');
// ... user interacts with the variant ...
// ... later when they convert ...
await client.track('purchase', userId, {
flagName: 'pricing-experiment',
variant: variant,
metadata: { amount: 99.99, currency: 'USD' }
});
// Track without flag association
await client.track('signup', userId, {
metadata: { source: 'organic' }
});
// Track simple event
await client.track('button_click', userId);const express = require('express');
const SetBitClient = require('@setbit-io/node');
const app = express();
const flagClient = new SetBitClient({
apiKey: process.env.SETBIT_API_KEY,
tags: { env: process.env.NODE_ENV }
});
app.get('/api/checkout', async (req, res) => {
const userId = req.user.id;
// Check feature flag
const useNewCheckout = await flagClient.enabled('new-checkout', userId, false);
if (useNewCheckout) {
res.json({ version: 'v2', features: ['express-checkout', 'saved-cards'] });
} else {
res.json({ version: 'v1', features: [] });
}
});
app.post('/api/purchase', async (req, res) => {
const userId = req.user.id;
const amount = req.body.amount;
const variant = req.body.variant; // Pass from frontend or re-evaluate
// Process purchase...
// Track conversion with variant attribution
await flagClient.track('purchase', userId, {
flagName: 'new-checkout',
variant: variant,
metadata: { amount }
});
res.json({ success: true });
});
app.listen(3000);// pages/api/features.js
import SetBitClient from '@setbit-io/node';
const client = new SetBitClient({
apiKey: process.env.SETBIT_API_KEY,
tags: { env: process.env.NODE_ENV }
});
export default async function handler(req, res) {
const userId = req.headers['x-user-id'];
const features = {
newUI: await client.enabled('new-ui', userId, false),
betaFeatures: await client.enabled('beta-features', userId, false),
experiment: await client.variant('pricing-experiment', userId, 'control')
};
res.status(200).json(features);
}// app/dashboard/page.tsx
import SetBitClient from '@setbit-io/node';
const client = new SetBitClient({
apiKey: process.env.SETBIT_API_KEY!
});
export default async function DashboardPage() {
const userId = 'user_123'; // From auth
const showNewDashboard = await client.enabled('new-dashboard', userId, false);
if (showNewDashboard) {
return <NewDashboard />;
}
return <OldDashboard />;
}const fastify = require('fastify')();
const SetBitClient = require('@setbit-io/node');
const flagClient = new SetBitClient({
apiKey: process.env.SETBIT_API_KEY
});
fastify.get('/api/features', async (request, reply) => {
const userId = request.user.id;
const variant = await flagClient.variant('ui-experiment', userId, 'control');
return {
theme: variant === 'dark' ? 'dark-mode' : 'light-mode',
variant
};
});
fastify.listen({ port: 3000 });The SDK is designed to fail open - if the API is unreachable or returns an error, methods return the specified default values rather than blocking your application.
// If API fails, returns false (safe default)
const enabled = await client.enabled('risky-feature', userId, false);
// If API fails, returns "control" (safe default)
const variant = await client.variant('experiment', userId, 'control');
// Track fails silently (logs error but doesn't block)
await client.track('event', userId, { flagName: 'flag', metadata: { key: 'value' } });Always provide a user ID for all flag evaluations. This is required for:
- Analytics and reporting
- Billing (MAU tracking)
- Consistent variant assignment in A/B tests
// β
Good - user ID provided
if (await client.enabled('feature', userId, false)) {
// ...
}
// β Bad - empty user ID
if (await client.enabled('feature', '', false)) {
// This will still work but won't be tracked properly
}// For new features, default to false (conservative)
if (await client.enabled('experimental-feature', userId, false)) {
// Only enable if API confirms
}
// For stable features, default to true (progressive)
if (await client.enabled('stable-feature', userId, true)) {
// Enable even if API is down
}Create one client instance and reuse it throughout your application:
// config/flags.js
const SetBitClient = require('@setbit-io/node');
const flagClient = new SetBitClient({
apiKey: process.env.SETBIT_API_KEY,
tags: {
env: process.env.NODE_ENV
}
});
module.exports = flagClient;
// Use in other files
const flagClient = require('./config/flags');
async function handler(req, res) {
if (await flagClient.enabled('feature', req.user.id, false)) {
// ...
}
}const client = new SetBitClient({
apiKey: 'pk_your_api_key',
tags: {
env: 'production', // Environment
app: 'api', // Application/service name
team: 'backend', // Team ownership
region: 'us-east-1' // Geographic region
}
});Run the test suite:
npm test- Node.js 18.0.0 or higher
- No external dependencies (uses only Node.js built-in modules)
MIT License - see LICENSE file for details
- π Documentation
- π¬ GitHub Issues
- π§ Email: support@setbit.io
Contributions are welcome! Please feel free to submit a Pull Request.