Skip to content

Node.js SDK

The @flagbridge/sdk-node package provides feature flag evaluation for Node.js and server-side TypeScript.

Installation

bash
npm install @flagbridge/sdk-node
bash
pnpm add @flagbridge/sdk-node
bash
yarn add @flagbridge/sdk-node

Requirements: Node.js 18+ · TypeScript 5+ (optional)

Quick start

typescript
import { FlagBridgeClient } from '@flagbridge/sdk-node';

const client = new FlagBridgeClient({
  apiKey: process.env.FLAGBRIDGE_API_KEY!,
  // baseUrl defaults to https://api.flagbridge.io
});

// Boolean flag
const enabled = await client.isEnabled('new-checkout-flow', {
  userId: 'user-123',
  plan: 'pro',
});

// Multi-variant flag
const variant = await client.getVariant('checkout-button-color', {
  userId: 'user-123',
});
// variant.value → "green" | "blue" | "red"
// variant.enabled → true

Configuration

typescript
const client = new FlagBridgeClient({
  // Required
  apiKey: 'fb_live_...',

  // Optional
  baseUrl: 'https://api.flagbridge.io', // or your self-hosted URL
  environment: 'production',            // override environment
  timeout: 5000,                        // request timeout in ms (default: 5000)
  cache: {
    ttl: 30_000,                        // in-memory cache TTL in ms (default: 30s)
    maxSize: 1000,                      // max cached entries
  },
  onError: (err) => {                   // custom error handler
    console.error('[FlagBridge]', err);
  },
});

INFO

By default, evaluation errors return false (flag disabled) so flag evaluation failures never break your application.

API

isEnabled(flagKey, context?)

Evaluates a boolean flag. Returns true if the flag is enabled for the given context.

typescript
const enabled = await client.isEnabled('my-flag', {
  userId: 'user-123',
  country: 'BR',
  plan: 'pro',
});
// → true | false

getVariant(flagKey, context?)

Evaluates a multi-variant flag and returns the variant result.

typescript
const result = await client.getVariant('button-color', {
  userId: 'user-123',
});

result.value;    // "green" — the variant value
result.enabled;  // true
result.reason;   // "TARGETING_RULE_MATCH" | "PERCENTAGE_ROLLOUT" | ...

evaluate(flagKey, context?)

Low-level evaluation — returns the full result object.

typescript
const result = await client.evaluate('my-flag', { userId: 'user-123' });

result.flagKey;   // "my-flag"
result.enabled;   // true
result.variant;   // null (or string for multi-variant flags)
result.reason;    // "TARGETING_RULE_MATCH"
result.ruleId;    // "rule_abc123"

evaluateBatch(flags, context?)

Evaluate multiple flags in a single request.

typescript
const results = await client.evaluateBatch(
  ['new-checkout', 'dark-mode', 'sidebar-v2'],
  { userId: 'user-123' }
);

results['new-checkout'].enabled; // true
results['dark-mode'].enabled;    // false

close()

Flush pending requests and close the client. Call when shutting down.

typescript
await client.close();

Caching

The SDK caches evaluation results in memory to avoid latency on every request. The cache is keyed by (flagKey, context hash).

typescript
const client = new FlagBridgeClient({
  apiKey: '...',
  cache: {
    ttl: 60_000,  // cache for 60 seconds
    maxSize: 500, // LRU eviction when this limit is reached
  },
});

To disable caching (useful in testing):

typescript
const client = new FlagBridgeClient({
  apiKey: '...',
  cache: false,
});

Testing helpers CE

The SDK ships with a TestingClient for use in your test suite. It uses the FlagBridge Testing API to create isolated sessions with per-flag overrides.

typescript
import { createTestingClient } from '@flagbridge/sdk-node/testing';

// In your test setup
const testClient = createTestingClient({
  apiKey: process.env.FLAGBRIDGE_TEST_API_KEY!,
  baseUrl: 'http://localhost:8080',
});

// Before each test
const session = await testClient.createSession();

// Override specific flags for this session
await session.override('new-checkout-flow', true);
await session.override('checkout-button-color', 'green');

// Run your test with the session token
// Pass the session token in your HTTP requests:
// X-FlagBridge-Session: <session.token>

// After each test
await session.destroy();

See the E2E testing guide for full integration examples with Playwright and Vitest.

Framework integrations

Express / Fastify

typescript
import { flagBridgeMiddleware } from '@flagbridge/sdk-node/middleware';

// Express
app.use(flagBridgeMiddleware({
  client,
  contextFromRequest: (req) => ({
    userId: req.user?.id,
    plan: req.user?.plan,
  }),
}));

// Now req.flags is available in your handlers
app.get('/checkout', async (req, res) => {
  if (req.flags.isEnabled('new-checkout-flow')) {
    return res.render('checkout-v2');
  }
  return res.render('checkout');
});

Next.js (App Router)

typescript
// lib/flags.ts
import { FlagBridgeClient } from '@flagbridge/sdk-node';

export const flagbridge = new FlagBridgeClient({
  apiKey: process.env.FLAGBRIDGE_API_KEY!,
});

// app/checkout/page.tsx
import { flagbridge } from '@/lib/flags';
import { cookies } from 'next/headers';

export default async function CheckoutPage() {
  const userId = (await cookies()).get('user_id')?.value;

  const newCheckout = await flagbridge.isEnabled('new-checkout-flow', {
    userId,
  });

  if (newCheckout) {
    return <NewCheckout />;
  }
  return <OldCheckout />;
}

TypeScript

All methods are fully typed. You can declare your flag keys for autocomplete and type safety:

typescript
import { FlagBridgeClient } from '@flagbridge/sdk-node';

// Declare your flag keys
type FlagKey =
  | 'new-checkout-flow'
  | 'checkout-button-color'
  | 'dark-mode';

const client = new FlagBridgeClient<FlagKey>({ apiKey: '...' });

// Now flagKey is typed
client.isEnabled('new-checkout-flow', { userId: 'user-123' }); // ✓
client.isEnabled('typo-flag', { userId: 'user-123' });          // ✗ TypeScript error

Error handling

By default, evaluation failures silently return false. Override with onError:

typescript
const client = new FlagBridgeClient({
  apiKey: '...',
  onError: (error) => {
    // Log to your error tracking
    Sentry.captureException(error);
  },
});

For explicit error handling, use evaluate() with try/catch:

typescript
try {
  const result = await client.evaluate('my-flag', { userId: 'user-123' });
} catch (err) {
  // Handle error explicitly
}

Changelog

See the SDK Node.js releases on GitHub.