Skip to content

setbit-io/setbit-node

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SetBit Node.js SDK

Official Node.js SDK for SetBit - Simple feature flags and A/B testing for server-side JavaScript.

Features

  • βœ… 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

Installation

npm install @setbit-io/node

Or with Yarn:

yarn add @setbit-io/node

Quick Start

const 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' }
});

TypeScript Usage

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' } });

API Reference

Constructor

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 key
  • tags (object, optional): Tags for targeting flags
  • baseURL (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

enabled(flagName, userId, defaultValue)

Check if a flag is enabled. Returns true if the flag is globally enabled, false otherwise.

Parameters:

  • flagName (string): Name of the flag
  • userId (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
}

variant(flagName, userId, defaultVariant)

Get the variant for an A/B test experiment or percentage rollout.

Parameters:

  • flagName (string): Name of the experiment flag
  • userId (string, required): User identifier
  • defaultVariant (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(eventName, userId, options)

Track a conversion event or user action.

Parameters:

  • eventName (string): Name of the event (e.g., "purchase", "signup", "click")
  • userId (string, required): User identifier
  • options (object, optional): Tracking options
    • flagName (string): Flag name to associate with event
    • variant (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);

Framework Examples

Express.js

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);

Next.js API Routes

// 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);
}

Next.js Server Components

// 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 />;
}

Fastify

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 });

Error Handling

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' } });

Best Practices

1. User ID is Required

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
}

2. Choose Appropriate Defaults

// 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
}

3. Reuse the Client Instance

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)) {
    // ...
  }
}

4. Use Tags for Organization

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
  }
});

Testing

Run the test suite:

npm test

Requirements

  • Node.js 18.0.0 or higher
  • No external dependencies (uses only Node.js built-in modules)

License

MIT License - see LICENSE file for details

Support

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

Node.js SDK for SetBit feature flagging

Resources

License

Stars

Watchers

Forks

Packages

No packages published