On this page

Node Core SDK Migration Guide

Migrate from the legacy Node JS Server SDK to the new Node JS Core SDK

Why migrate?

  • Performance: Node JS Core evaluates faster than the legacy SDK.
  • New Features: Access to Parameter Stores, CMAB (Contextual Multi-Armed Bandits), observabilityClient, and more.
  • Future Support: All new features and improvements are only available in Node JS Core.
  • Maintenance: The legacy Node JS SDK is in maintenance mode and only receives critical bug fixes.

Installation

npm install @statsig/statsig-node-core

StatsigUser

In the Node Core SDK, StatsigUser is now a class instead of a TypeScript type interface. This is a key architectural change that affects how you create user objects.

Type definitions

// StatsigUser is a class with a constructor
class StatsigUser {
  constructor(
    args: ({ userID: string } | { customIDs: Record<string, string> }) &
      StatsigUserArgs,
  );

  // Static factory methods
  static withUserID(userId: string): StatsigUser;
  static withCustomIDs(customIds: Record<string, string>): StatsigUser;

  // Properties with getters/setters
  userID: string | null;
  customIDs: Record<string, string> | null;
  email: string | null;
  ip: string | null;
  userAgent: string | null;
  country: string | null;
  locale: string | null;
  appVersion: string | null;
  custom: Record<
    string,
    string | number | boolean | Array<string | number | boolean>
  > | null;
  privateAttributes: Record<
    string,
    string | number | boolean | Array<string | number | boolean>
  > | null;
  statsigEnvironment: {
    tier?: string;
    [key: string]: string | undefined;
  } | null;

  toJSON(): string;
}

Creating users

import { StatsigUser } from "@statsig/statsig-node-core";

// Option 1: Using constructor with object
const user = new StatsigUser({
  userID: "user_123",
  email: "user@example.com",
  ip: "192.168.0.1",
  userAgent: "Mozilla/5.0",
  country: "US",
  custom: {
    subscription_level: "premium",
  },
});

// Option 2: Using static factory method
const userById = StatsigUser.withUserID("user_123");

// Option 3: Using customIDs
const userByCustomId = StatsigUser.withCustomIDs({ companyID: "company_456" });

Key Difference: In Node Core, you must use new StatsigUser({...}) to create a user object. Plain objects won't work and cause type errors.

API changes

Key package and class changes

For more details on the new API, refer to the Node Core SDK documentation.

Singleton pattern changes

The Node Core SDK provides a different approach to singleton management compared to the legacy SDK.

import { Statsig, StatsigUser } from "@statsig/statsig-node-core";

// Option 1: Instance-based (recommended for most use cases)
const statsig = new Statsig("secret-key");
await statsig.initialize();

// Option 2: Shared singleton pattern
Statsig.newShared("secret-key", options); // Creates the shared instance
await Statsig.shared().initialize(); // Access via Statsig.shared()

// Check if shared instance exists
if (Statsig.hasShared()) {
  const result = Statsig.shared().checkGate(user, "my_gate");
}

// Remove shared instance when done
Statsig.removeSharedInstance();

Key Difference: The legacy SDK uses a single global singleton that you access directly using Statsig.methodName(). The Node Core SDK supports both instance-based usage (new Statsig()) and an explicit shared singleton pattern (Statsig.newShared() / Statsig.shared()). The instance-based approach is recommended because it provides better control over SDK lifecycle.

Config Value Access with get() Method

Starting in version 0.10.0, the Node Core SDK supports the get() method on DynamicConfig, Experiment, and Layer objects, matching the legacy SDK's API.

import { Statsig, StatsigUser } from "@statsig/statsig-node-core";

const statsig = new Statsig("secret-key");
await statsig.initialize();

const user = new StatsigUser({ userID: "user_123" });

// Get a dynamic config
const config = statsig.getDynamicConfig(user, "my_config");

// Access values using get() with type-safe fallback (available in 0.10.0+)
const stringValue = config.get("param_name", "default_string");
const numberValue = config.get("count", 0);
const boolValue = config.get("enabled", false);

// Alternative: getValue() also available
const value = config.getValue("param_name", "default");

// Same pattern works for experiments
const experiment = statsig.getExperiment(user, "my_experiment");
const variant = experiment.get("variant", "control");

// And layers
const layer = statsig.getLayer(user, "my_layer");
const layerValue = layer.get("param", "default");

The get<T>(paramName, fallback) method provides type inference based on the fallback value type, making it easier to work with typed configuration values.

Complete sample code

The following are complete, working examples showing the same functionality in both the legacy Node SDK and the Node Core SDK.

import {
  Statsig,
  StatsigUser,
  StatsigOptions,
} from "@statsig/statsig-node-core";

async function main() {
  // 1. Configure options
  const options: StatsigOptions = {
    environment: "development",
    specsSyncIntervalMs: 60000,
    eventLoggingFlushIntervalMs: 10000,
  };

  // 2. Create SDK instance (instance-based approach)
  const statsig = new Statsig("secret-your-server-key", options);

  // 3. Initialize the SDK
  await statsig.initialize();

  // 4. Create a user (must use class constructor)
  const user = new StatsigUser({
    userID: "user_123",
    email: "user@example.com",
    custom: {
      subscription_tier: "premium",
      signup_date: "2024-01-15",
    },
  });

  // 5. Check a feature gate
  const showNewFeature = statsig.checkGate(user, "new_feature_gate");
  console.log("Show new feature:", showNewFeature);

  // 6. Get a dynamic config (note: getDynamicConfig, not getConfig)
  const config = statsig.getDynamicConfig(user, "app_config");
  const welcomeMessage = config.get("welcome_message", "Hello!");
  const maxItems = config.get("max_items", 10);
  console.log("Welcome message:", welcomeMessage);
  console.log("Max items:", maxItems);

  // 7. Get an experiment
  const experiment = statsig.getExperiment(user, "checkout_flow_test");
  const variant = experiment.get("variant", "control");
  const buttonColor = experiment.get("button_color", "#007bff");
  console.log("Experiment variant:", variant);
  console.log("Button color:", buttonColor);

  // 8. Get a layer
  const layer = statsig.getLayer(user, "homepage_layer");
  const heroText = layer.get("hero_text", "Welcome to our app");
  console.log("Hero text:", heroText);

  // 9. Log a custom event
  statsig.logEvent(user, "purchase_completed", 49.99, {
    item_id: "SKU_12345",
    currency: "USD",
  });

  // 10. Shutdown gracefully
  await statsig.shutdown();
  console.log("Statsig shutdown complete");
}

main().catch(console.error);

Behavioral changes

StatsigOptions changes

The table below shows the mapping between legacy SDK options and Server Core SDK options:

  1. Add the new Dependencies

    • Add the new SDK package/module and any required platform-specific dependencies for your environment.
    • Update import or require statements to reference the new SDK namespace or module.
    • Keep the old package in place during local testing; having both running in parallel briefly can help. Alias the new package if needed.
  2. Update Initialization

    • Switch to the new initialization syntax. New SDK keys are not required. If you update them, ensure they have the same target apps.
    • Use the appropriate builder or configuration pattern for setting options, and migrate the StatsigOptions you use, because some were renamed.
  3. Update User Creation

    • Migrate to the new format for creating user objects.
    • Use the builder pattern or updated constructor to set user properties if needed.
  4. Update Method Calls

    • Start by migrating a few calls, replacing oldStatsig.getExperiment() with newStatsig.getExperiment().
    • Test your service locally or with existing test patterns to build confidence in the new SDK's operation.
    • As you migrate additional calls, update method names that changed (notably, get_config() to get_dynamic_config()).
    • After completing the initial migration, use refactoring tools to migrate remaining calls in bulk.
  5. Test Thoroughly

    • Verify that all of your configs are successfully migrated - run your test suites end-to-end.
  6. Remove old SDK

    • Remove references to the old SDK, and clean up old initialization and import logic.

Need help?

If you encounter any issues during migration, reach out:

Was this helpful?