JavaScript SDK

The @flagify/node package is the core SDK for evaluating feature flags in any JavaScript or TypeScript runtime.

Installation

npm install @flagify/node

Initialization

import { Flagify } from '@flagify/node';

const flagify = new Flagify({
  projectKey: 'my-project',
  publicKey: 'pk_dev_abc123_xxxxxxxx',
  options: {
    realtime: true,       // enable SSE streaming for live updates
    staleTimeMs: 300000,  // cache stale time (5 min)
  },
});

Configuration options

OptionTypeDefaultDescription
projectKeystringYour project key (required)
publicKeystringPublishable API key (required)
secretKeystringSecret API key (server-side only)
options.realtimebooleanfalseEnable real-time flag updates via SSE
options.staleTimeMsnumberCache stale threshold in ms
options.apiUrlstringhttps://api.flagify.devCustom API base URL
options.userFlagifyUserUser context for targeting

Evaluating flags

Boolean flags

const isEnabled = flagify.isEnabled('feature-name');

isEnabled() returns the flag’s evaluated value (not the enabled state). When the flag is disabled, it returns the flag’s offValue. When the flag doesn’t exist in cache, it returns false.

Getting a flag value

const maxRetries = flagify.getValue<number>('max-retries', 3);

When the flag is enabled, getValue() returns the flag’s current value. When disabled, it returns the flag’s offValue from the server. The fallback parameter is only used when the flag doesn’t exist in cache.

Multivariate flags (A/B testing)

Use getVariant() to get the winning variant key based on weight:

const variant = flagify.getVariant('checkout-flow', 'control');
// Returns the variant key with the highest weight, e.g. 'variant-a'

Real-time updates (SSE)

When realtime: true is set, the SDK opens a Server-Sent Events connection to the Flagify API. When a flag is changed (toggled, updated, promoted), the SDK automatically refetches that specific flag and updates the cache.

const flagify = new Flagify({
  projectKey: 'my-project',
  publicKey: 'pk_dev_abc123_xxxxxxxx',
  options: {
    realtime: true,
  },
});

// Flags are updated automatically — no polling needed
const enabled = flagify.isEnabled('new-checkout');

The SSE connection:

  • Reconnects automatically with exponential backoff (1s to 30s)
  • Receives heartbeats every 30s to keep the connection alive
  • Only refetches the specific flag that changed (not all flags)

User context

The user context object supports built-in and custom attributes for future targeting:

const flagify = new Flagify({
  projectKey: 'my-project',
  publicKey: 'pk_dev_abc123_xxxxxxxx',
  options: {
    user: {
      id: 'user-123',
      email: '[email protected]',
      role: 'admin',
      plan: 'pro',
    },
  },
});

Evaluating with targeting

Use evaluate() to check a flag against targeting rules with user context. This calls the API directly (not cached):

const result = await flagify.evaluate('checkout-flow', {
  id: 'user-123',
  plan: 'pro',
  country: 'US',
});

// result.value — the matched value
// result.reason — "targeting_rule", "rollout", "default", or "disabled"

Listening for flag changes

Subscribe to flag change events using the event emitter pattern:

const unsubscribe = flagify.onFlagChange((event) => {
  console.log(`Flag ${event.flagKey} was ${event.action}`);
});

// Later, unsubscribe when no longer needed
unsubscribe();

You can register multiple listeners. Each call to onFlagChange() returns an unsubscribe function.

Shutdown

Disconnect the realtime listener and clean up all resources:

flagify.destroy();

TypeScript types

All types are exported from @flagify/node:

import type {
  FlagifyOptions,
  FlagifyUser,
  FlagifyFlag,
  IFlagifyClient,
  FlagChangeEvent,
  EvaluateResult,
} from '@flagify/node';

FlagifyOptions

interface FlagifyOptions {
  projectKey: string;
  publicKey: string;
  secretKey?: string;
  options?: {
    user?: FlagifyUser;
    apiUrl?: string;
    staleTimeMs?: number;
    realtime?: boolean;
  };
}

FlagifyUser

interface FlagifyUser {
  id: string;
  email?: string;
  role?: string;
  group?: string;
  geolocation?: {
    country?: string;
    region?: string;
    city?: string;
  };
  [key: string]: unknown; // custom attributes
}

FlagifyFlag

The shape of a flag returned by the API:

interface FlagifyFlag {
  key: string;
  name: string;
  value: boolean | string | number | Record<string, unknown>;
  description?: string;
  type: 'boolean' | 'string' | 'number' | 'json';
  defaultValue: boolean | string | number | Record<string, unknown>;
  offValue: boolean | string | number | Record<string, unknown>;
  enabled: boolean;
  rolloutPercentage?: number;
  targetingRules?: Array<{
    priority: number;
    segmentId?: string;
    valueOverride?: unknown;
    rolloutPercentage?: number;
    rolloutSalt?: string;
    enabled: boolean;
    matchType?: 'ALL' | 'ANY';
    conditions?: Array<{
      attribute: string;
      operator: 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'in' | 'not_in' | 'gt' | 'lt';
      value: unknown;
    }>;
  }>;
  variants?: Array<{
    key: string;
    value: boolean | string | number | Record<string, unknown>;
    weight: number;
  }>;
  createdAt: string;
  updatedAt: string;
}

IFlagifyClient

interface IFlagifyClient {
  getValue<T>(flagKey: string, fallback: T): T;
  isEnabled(flagKey: string): boolean;
  getVariant(flagKey: string, fallback: string): string;
  evaluate(flagKey: string, user: FlagifyUser): Promise<EvaluateResult>;
}

EvaluateResult

interface EvaluateResult {
  key: string;
  value: unknown;
  reason: 'targeting_rule' | 'rollout' | 'default' | 'disabled';
}