Overview

The @flagsync/node-sdk integrates into Node.js applications for server-side feature management and event tracking—ideal for backend services and APIs.

Installation

Install the SDK with your preferred package manager:

npm install @flagsync/node-sdk

Quickstart

A basic example of using the SDK in a Node.js application (e.g., an Express server):

import express, { Request, Response } from 'express';

import { FlagSyncFactory, FsUserContext } from '@flagsync/node-sdk';

import { getFlagSyncUserContext } from '@/lib/flagsync.user-context.js';


const app = express();

const port = 3000;


// Initialize the SDK

const factory = FlagSyncFactory({ sdkKey: 'your-sdk-key'});


// Get the client

const client = factory.client();


// Wait for SDK to be ready

await client.waitForReady();


app.get('/', async (req: Request, res: Response) => {

  const context = getFlagSyncUserContext(req);


  // Evaluate a flag with user context

  const isEnabled: boolean = client.flag(context, 'feature-enabled', false);


  if (isEnabled) {

    res.send('Feature is ON');

  } else {

    res.send('Feature is OFF');

  }

});


app.listen(port, () => {

  console.log(`Server is running on http://localhost:${port}`);

});

See User Context Identification for details on the getFlagSyncUserContext object.

Initialization

Get Your SDK Key

Find your server-side SDK key in your workspace settings. Keep this key private and secure.

Initialize the SDK

Initialize the SDK with your SDK key:

import { FlagSyncFactory } from '@flagsync/node-sdk';


const factory = FlagSyncFactory({

  sdkKey: 'your-sdk-key'

});


const client = factory.client();

Wait for Readiness

Use promises to ensure the SDK is ready before evaluating flags:

1

Promises

await client.waitForReady();

// SDK is ready
2

Events

import { FsEvent } from '@flagsync/node-sdk';


client.once(FsEvent.SDK_READY, () => {

  // SDK is ready

});

User Context Identification

Define the user context with a helper function that returns an FsUserContext object. Pass this object to flag() and track() functions.

User contexts enable personalized flag evaluations via Individual Targeting and consistent experiences during Percentage Rollouts.

Ensure the key in FsUserContext is unique and persistent for accurate MAU tracking and consistent flag evaluations. See User Context Best Practices for details.

1

Create a Helper Function

Create a helper function to construct the user context from request cookies and headers.

lib/flagsync.user-context.js
import { nanoid } from 'nanoid';


export function getFlagSyncUserContext(req) {

  const userId = req.cookies['user-id'];

  const visitorId = req.cookies['visitor-id'];


  return {

    key: userId ?? visitorId ?? nanoid(),

    attributes: {

      userAgent: req.headers['user-agent'],

      region: req.headers['x-region'],

    }

  };

}

The key is set using a persistent userId or visitorId from cookies, falling back to a generated ID with nanoid(). For proper MAU tracking and consistent flag evaluations, ensure this key is unique and persistent across requests—see User Context Best Practices.

2

Set Cookies in Middleware (Optional)

Use Express middleware to set user identification cookies, simplifying context retrieval in the helper function.

Middleware is optional—user identification logic can be implemented directly in getFlagSyncUserContext() or elsewhere in your application.

lib/flagsync.identify.js
import { nanoid } from 'nanoid';

import { verify } from 'jsonwebtoken';


const MAX_AGE = 60 * 60 * 24 * 7; // 7 days


export function identifyUser(req, res, next) {

  const jwt = req.cookies['jwt'];

  const visitorId = req.cookies['visitor-id'] ?? nanoid();



  // Replace this with your own logic to identify the user

  let user;

  if (jwt) {

    try {

      user = verify(jwt, process.env.JWT_SECRET);

    } catch (error) {

      console.error('JWT verification failed:', error);

    }

  }


  if (user?.userId) {

    // Set the user-id cookie if the JWT contains a userId

    res.cookie('user-id', user.userId, { maxAge: MAX_AGE, httpOnly: true });

  } else {

    // Set the visitor-id cookie if the JWT does not contain a userId

    res.cookie('visitor-id', visitorId, { maxAge: MAX_AGE, httpOnly: true });

  }


  next();

}
3

Use the Helper in Routes

In your Express routes, use the helper to build the FsUserContext for flag evaluations or event tracking.

app.js
import express from 'express';

import cookieParser from 'cookie-parser';

import { identifyUser } from '@/lib/flagsync.identify.js';

import { getFlagSyncUserContext } from '@/lib/flagsync-context.js';


const app = express();

app.use(cookieParser());

app.use(identifyUser);  // Identify the user


app.post('/feature', async (req, res) => {

  const context = getFlagSyncUserContext(req);

  const isEnabled = client.flag(context, 'feature-enabled', false);

  res.status(200).json({ enabled: isEnabled });

});

Usage

Evaluate Flags

Evaluate flags with flag(), which applies targeting rules, rollouts, and defaults for the user context:

client.flag<T>(context: FsUserContext, flagKey: string, defaultValue?: T);

An Impression is automatically registered when flag() is called.

SDK Not Ready

If the SDK isn’t ready, it returns the defaultValue or control:

// SDK not ready

const one = client.flag(context, 'flag-one', false); // false (defaultValue)

const two = client.flag(context, 'flag-two');        // "control"

SDK Ready

Once ready, flag() returns the server-evaluated value:

await client.waitForReady();

const value = client.flag(context, 'flag-one'); // Server-evaluated value

Track Events

Submit user actions with the track() function:

app.post('/checkout', async (req: Request, res: Response) => {

  const request: PurchaseRequest = req.body;

  const context: FsUserContext = {

    key: 'user-123',

    email: 'user@example.com',

  };


  client.track(context, 'purchase-request', null, request);


  const t0 = Date.now();

  const order = await makePurchase(request);

  const receipt = await sendOrder(order);

  const t1 = Date.now();


  client.track(context, 'purchase-duration', t1 - t0);


  res.status(200).json({ success: true, receipt });

});

See Events: Tracking to learn about numeric and property events.

SDK Event Listeners

The SDK emits these events for SDK lifecycle management:

  • SDK_UPDATE: Emitted when flags are updated
  • SDK_READY: Emitted when the SDK is ready
  • ERROR: Emitted when an error occurs during initialization
import { FsEvent } from '@flagsync/node-sdk';


// Flag updates

client.on(FsEvent.SDK_UPDATE, () => {

  console.log(`Flags updated at ${new Date().toISOString()}`);

});

SDK_UPDATE does not fire if syncing is disabled.

Error Handling

import { FsServiceError } from '@flagsync/node-sdk';


// Via events

client.on(FsEvent.ERROR, (error: FsServiceError) => {

  console.error('SDK Error:', error);

});


// Via promises

try {

  await client.waitForReadyCanThrow();

} catch (error) {

  console.error('Initialization Error:', error as FsServiceError);

}

All exposed errors are normalized to FsServiceError.

Configuration

Configure the SDK with the FsConfig interface:

export interface FsConfig {

  sdkKey: string;

  sync?: {

    type?: 'stream' | 'poll' | 'off'; // Optional: Sync strategy

    pollRate?: number;                // Optional: Polling interval in seconds

  };

  tracking?: {

    impressions?: {

      maxQueueSize: number;           // Required: Max impressions queue size

      pushRate: number;               // Required: Impressions push rate

    };

    events?: {

      maxQueueSize: number;           // Required: Max events queue size

      pushRate: number;               // Required: Events push rate

    };

  };

  urls?: {

    sdk?: string;                     // Optional: SDK endpoint URL

  };

  logger?: Partial<ILogger>;          // Optional: Custom logger

  logLevel?: LogLevel;                // Optional: Logging level

  metadata?: Record<string, any>;     // Optional: Additional metadata

}

Custom Attributes

const context: FsUserContext = {

  key: 'user-123',

  attributes: {

    plan: 'premium',

    country: 'US',

    userType: 'enterprise'

  }

}

Ensure the key in FsUserContext is unique and persistent for accurate MAU tracking and consistent flag evaluations. See User Context Best Practices for details.

Flag Syncing

Configure flag update strategies with the sync object: stream, poll, or off.

By default, flag updates propagate in milliseconds via server-side events (SSE), ensuring the latest values are used in evaluations.

1

Stream (Default)

Stream updates via SSE—flag updates are reevaluated on the server and sent to the client:

const factory = FlagSyncFactory({

  sdkKey: 'your-sdk-key',

  sync: {

    type: 'stream'

  }

});
2

Polling

Poll the server on an interval:

const factory = FlagSyncFactory({

  sdkKey: 'your-sdk-key',

  sync: {

    type: 'poll',

    pollRate: 60

  }

});
3

Off

Disable syncing:

const factory = FlagSyncFactory({

  sdkKey: 'your-sdk-key',

  sync: {

    type: 'off'

  }

});

Best Practices

  • Wait for SDK readiness before evaluating flags.
  • Select a sync strategy (stream/poll/off) based on your application’s needs.
  • Follow User Context Identification for simplified context management.
  • Add user attributes for targeted feature rollouts.

Environment Variables

Set the following environment variable:

  • FLAGSYNC_SDK_KEY: Your server-side FlagSync SDK key (required)