> ## Documentation Index
> Fetch the complete documentation index at: https://statsig-4b2ff144-mintlify-add-local-eval-country-note-96362.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Node Core SDK Migration Guide

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

export const waitForUserAgentInit_0 = "waitForUserAgentInit"

export const waitForCountryLookupInit_0 = "waitForCountryLookupInit"

export const disableUserAgentParsing_0 = "disableUserAgentParsing"

export const disableCountryLookup_0 = "disableCountryLookup"

export const enableIdLists_0 = "enableIdLists"

export const sdkType_0 = "Node JS"

## Why Migrate?

* **Performance**: {sdkType_0} 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 will only be available in {sdkType_0} Core
* **Maintenance**: The legacy {sdkType_0} SDK is in maintenance mode and will only receive critical bug fixes

## Installation

<CodeGroup>
  ```bash Node Core theme={null}
  npm install @statsig/statsig-node-core
  ```

  ```bash Node Legacy theme={null}
  npm install statsig-node
  ```
</CodeGroup>

## 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

<CodeGroup>
  ```typescript Node Core (Class) theme={null}
  // 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
  }
  ```

  ```typescript Node Legacy (Type) theme={null}
  // StatsigUser is a TypeScript type (plain object)
  type StatsigUser = 
    // At least one of userID or customIDs must be provided
    ({ userID: string } | { customIDs: Record<string, string> }) & {
      userID?: string;
      customIDs?: Record<string, string>;
      email?: string;
      ip?: string;
      userAgent?: string;
      country?: string;
      locale?: string;
      appVersion?: string;
      custom?: Record<string, string | number | boolean | Array<string> | undefined>;
      privateAttributes?: Record<string, string | number | boolean | Array<string> | undefined> | null;
      statsigEnvironment?: StatsigEnvironment;
    };
  ```
</CodeGroup>

### Creating Users

<CodeGroup>
  ```typescript Node Core theme={null}
  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" });
  ```

  ```typescript Node Legacy theme={null}
  import { StatsigUser } from "statsig-node";

  // Plain object - no constructor needed
  const user: StatsigUser = {
      userID: "user_123",
      email: "user@example.com",
      ip: "192.168.0.1",
      userAgent: "Mozilla/5.0",
      country: "US",
      custom: {
          subscription_level: "premium"
      }
  };

  // Using customIDs
  const userByCustomId: StatsigUser = {
      customIDs: { companyID: "company_456" }
  };
  ```
</CodeGroup>

<Note>
  **Key Difference**: In Node Core, you must use `new StatsigUser({...})` to create a user object. Plain objects will not work and will cause type errors.
</Note>

## API Changes

### Key Package and Class Changes

| Feature        | Node Core SDK                | Legacy Node SDK              | Status                        |
| -------------- | ---------------------------- | ---------------------------- | ----------------------------- |
| Package        | `@statsig/statsig-node-core` | `statsig-node`               | ⚠️ Renamed                    |
| Options        | `StatsigOptions`             | `StatsigOptions`             | ✓ Same                        |
| User           | `StatsigUser`                | `StatsigUser`                | ⚠️ Changed from type to class |
| Initialize     | `await statsig.initialize()` | `await statsig.initialize()` | ✓ Same                        |
| Check Gate     | `statsig.checkGate()`        | `statsig.checkGate()`        | ✓ Same                        |
| Get Config     | `statsig.getDynamicConfig()` | `statsig.getConfig()`        | ⚠️ Renamed                    |
| Get Experiment | `statsig.getExperiment()`    | `statsig.getExperiment()`    | ✓ Same                        |
| Get Layer      | `statsig.getLayer()`         | `statsig.getLayer()`         | ✓ Same                        |
| Log Event      | `statsig.logEvent()`         | `statsig.logEvent()`         | ✓ Same                        |
| Shutdown       | `await statsig.shutdown()`   | `await statsig.shutdown()`   | ✓ Same                        |

For more details on the new API, see [Node Core SDK documentation](/server-core/node-core).

### Singleton Pattern Changes

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

<CodeGroup>
  ```typescript Node Core (Instance-based + Shared Singleton) theme={null}
  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();
  ```

  ```typescript Node Legacy (Global Singleton) theme={null}
  // Single global singleton - no instance creation needed
  await Statsig.initialize("secret-key", options);

  // All methods called directly on the Statsig object
  const result = Statsig.checkGate(user, "my_gate");

  // Shutdown the global instance
  await Statsig.shutdown();
  ```
</CodeGroup>

<Note>
  **Key Difference**: The legacy SDK uses a single global singleton that you access directly via `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 as it provides better control over SDK lifecycle.
</Note>

### Config Value Access with `get()` Method

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

<CodeGroup>
  ```typescript Node Core (0.10.0+) theme={null}
  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");
  ```

  ```typescript Node Legacy theme={null}
  const Statsig = require("statsig-node");

  await Statsig.initialize("secret-key");

  const user = { userID: "user_123" };

  // Get a dynamic config (note: getConfig vs getDynamicConfig)
  const config = Statsig.getConfig(user, "my_config");

  // Access values using get() with type-safe fallback
  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");
  ```
</CodeGroup>

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

## Complete Sample Code

Below are complete, working examples showing the same functionality implemented in both the legacy Node SDK and the new Node Core SDK.

<CodeGroup>
  ```typescript Node Core (Complete Example) theme={null}
  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);
  ```

  ```typescript Node Legacy (Complete Example) theme={null}
  const Statsig = require("statsig-node");

  async function main() {
      // 1. Configure options
      const options = {
          environment: { tier: "development" },
          rulesetsSyncIntervalMs: 60000,
          loggingIntervalMs: 10000,
      };

      // 2. Initialize the SDK (global singleton)
      await Statsig.initialize("secret-your-server-key", options);

      // 3. Create a user (plain object)
      const user = {
          userID: "user_123",
          email: "user@example.com",
          custom: {
              subscription_tier: "premium",
              signup_date: "2024-01-15"
          }
      };

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

      // 5. Get a dynamic config (note: getConfig, not getDynamicConfig)
      const config = Statsig.getConfig(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);

      // 6. 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);

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

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

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

  main().catch(console.error);
  ```
</CodeGroup>

## Behavioral Changes

<AccordionGroup>
  <Accordion title="User-Agent Parsing">
    The SDK now uses a lightweight YAML-based User-Agent parser based on a reduced version of the [ua-parser regex definitions](https://github.com/statsig-io/statsig-server-core/blob/00ad0e4024ca5d30f21892c8f2f23e836165a509/statsig-rust/resources/ua_parser_regex_lite.yaml#L4).

    This parser supports the following commonly used browsers:

    * Chrome
    * Firefox & Firefox Mobile
    * Safari & Mobile Safari
    * Chrome Mobile
    * Android
    * Edge & Edge Mobile
    * IE Mobile
    * Opera Mobile

    If your use case requires identifying less common browsers, you should parse the User-Agent externally before sending it to Statsig.
  </Accordion>

  <Accordion title="Lazy Initialization: User-Agent Parser & Country Lookup">
    User-Agent parsing and country lookup are now **lazy-loaded by default** to improve SDK initialization performance.

    If your application relies on these features being ready **immediately after** `initialize()`, you can opt in by setting:

    * {waitForUserAgentInit_0}
    * {waitForCountryLookupInit_0}

    ⚠️ Enabling these options will increase total initialization time.

    To **disable** these features entirely, set the `StatsigOptions` flags {disableUserAgentParsing_0} and {disableCountryLookup_0}.
  </Accordion>

  <Accordion title="ID List Feature">
    The ID List functionality is **disabled by default**.

    To enable it, set the `StatsigOptions` flag {enableIdLists_0}.
  </Accordion>

  <Accordion title="Endpoint Changes (v1 to v2)">
    The core SDK now fetches configuration specs from a new endpoint:

    * **Old**: `v1/download_config_specs`
    * **New**: `v2/download_config_specs`

    If you are hosting your own **pass-through proxy**, ensure it supports and correctly routes the `v2` endpoint.

    * If you are using the **Statsig Forward Proxy (SFP)**, this endpoint migration is handled automatically.
  </Accordion>

  <Accordion title="Environment Evaluation">
    There are different places you can define environment StatsigUser and StatsigOptions, server-core try StatsigUser then StatsigOptions, then default to production.
    Node SDK has different orders: StatsigOptions -> StatsigUser -> default to production.
  </Accordion>

  <Accordion title="DataStore cache format">
    \*\* New DataStore cache format\*\*

    * **Old**: (Except Node v6+)
      * config\_spec cache key is: statsig.cache
      * IDLists statsig.id\_lists
    * **New**
      * config\_spec cache key:  `statsig|/v2/download_config_specs|plain_text|{SHA256HashedBase64(secretkey)}`
      * IDLists: We don't support idlist data store.
  </Accordion>
</AccordionGroup>

## StatsigOptions Changes

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

| Old Option                                                                                 | New / Notes                                                                                  |
| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| `api`                                                                                      | Deprecated                                                                                   |
| `idlists_thread_limit`, `logging_interval`, `enable_debug_logs`, `events_flushed_callback` | Deprecated                                                                                   |
| `timeout`                                                                                  | Deprecated (was only for network)                                                            |
| `apiForDownloadConfigSpecs`                                                                | `specsUrl` – must complete with endpoint                                                     |
| `apiForIdLists`                                                                            | `idListsUrl` – must complete with endpoint                                                   |
| `apiForLogEvent`                                                                           | `logEventUrl` – must complete with endpoint                                                  |
| `initTimeoutMs`                                                                            | `initTimeoutMs` – applies to overall initialization (suggest adding +1000 ms if enabling UA) |
| `rulesetsSyncIntervalMs`                                                                   | `specsSyncIntervalMs` (unit: milliseconds)                                                   |
| `idListsSyncIntervalMs`                                                                    | `idListsSyncIntervalMs` (unit: milliseconds)                                                 |
| `loggingIntervalMs`                                                                        | `eventLoggingFlushIntervalMs`                                                                |
| `loggingMaxBufferSize`                                                                     | `eventLoggingMaxQueuesize`                                                                   |
| `dataAdapter`                                                                              | Still supported but interface changed                                                        |
| `observability_client`, `sdk_error_callback`                                               | Now within `observability_client` interface                                                  |
| `logger`                                                                                   | Now `OutputLoggerProvider` interface                                                         |
| `bootstrap_values`, `evaluation_callback`, `rules_updated_callback`                        | Not yet supported                                                                            |

## Recommended Migration Path

<Steps>
  <Step title="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.
    * For now - keep the old package in-place, it can be helpful to have both running in parallel momentarily during local testing for your upgrade. If needed (language dependent), you may want to alias the new package.
  </Step>

  <Step title="Update Initialization">
    * Switch to the new initialization syntax. New SDK keys aren't needed - if you choose to update them, ensure they have the same target apps.
    * If needed, use the appropriate builder or configuration pattern for setting options, and ensure you migrate the StatsigOptions you use, as some were renamed.
  </Step>

  <Step title="Update User Creation">
    * Migrate to the new format for creating user objects.
    * If needed, use the builder pattern or updated constructor to set user properties.
  </Step>

  <Step title="Update Method Calls">
    * Starting with a few calls, switch your config checks from the new to old SDK, replacing oldStatsig.getExperiment() with newStatsig.getExperiment()
    * As you change the package usage, conduct some testing of 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 if they've changed (notably, get\_config() to get\_dynamic\_config)
    * Once comfortable with the operation of the new SDK from call-by-call migration, consider the use of refactoring tools to migrate the bulk of your remaining calls.
  </Step>

  <Step title="Test Thoroughly">
    * Verify that all of your configs are successfully migrated - run your test suites end-to-end.
  </Step>

  <Step title="Remove old SDK">
    * Remove references to the old SDK, and clean up old initialization and import logic.
  </Step>
</Steps>

## Need Help?

If you encounter any issues during migration, please reach out to us:

* [Statsig Slack Community](https://statsig.com/slack)
* [GitHub Issues](https://github.com/statsig-io/statsig-server-core/issues)
