Node Server SDK
Statsig's next-gen Node Server SDK built on our [Server Core](/server-core) framework
Set up the SDK
Install the SDK
shellnpm i @statsig/statsig-node-coreThe Node SDK is pre-built and compiled for different operating systems and CPU architectures. Package managers resolve the correct version automatically.
Initialize the SDK
After installation, initialize the SDK using a Server Secret Key from the Statsig console.Keep Server Secret Keys private. If you expose one, you can disable and recreate it in the Statsig console.
The optional
optionsparameter accepts aStatsigOptionsobject to customize the SDK.jsx// Basic initialization import { Statsig, StatsigUser } from '@statsig/statsig-node-core'; //Or, in common JS, const { Statsig, StatsigUser } = require('@statsig/statsig-node-core'); const statsig = new Statsig("secret-key"); await statsig.initialize(); // or with StatsigOptions const options: StatsigOptions = { environment: "staging" }; const statsigWithOptions = new Statsig("secret-key", options); await statsigWithOptions.initialize();initializeperforms a network request. Afterinitializecompletes, virtually all SDK operations are synchronous (refer to Evaluating Feature Gates in the Statsig SDK). The SDK fetches updates from Statsig in the background independently of API calls.
Working with the SDK
Checking a Feature Flag/Gate
After the SDK is initialized, you can fetch a Feature Gate. Feature Gates create logic branches in code that you can roll out to different users from the Statsig Console. Gates are always CLOSED or OFF (equivalent toreturn false;) by default.All APIs require a user object (refer to Statsig user). For example:const user = new StatsigUser({ userID: "a-user" });
if (statsig.checkGate(user, "a_gate")) {
// Gate is on, enable new feature
} else {
// Gate is off
}
Reading a Dynamic Config
Feature Gates are useful for on/off switches with optional user targeting. To send different values (strings, numbers, and similar types) to clients based on user attributes such as country, use Dynamic Configs. The API is similar to Feature Gates, but returns a full JSON object configurable on the server, from which you can fetch typed parameters.// Get the dynamic config
const config = statsig.getDynamicConfig(user, "a_config");
// Get typed values using the getValue() method
const itemName = config.getValue("product_name", "Awesome Product v1");
const price = config.getValue("price", 10.0);
const shouldDiscount = config.getValue("discount", false);
// Or access the entire value object directly
const value = config.value;
Getting a Layer/Experiment
Use Layers/Experiments to run A/B/n experiments. Two APIs are available. Statsig recommends Layers because they make parameters reusable and support mutually exclusive experiments.// Or, via individual experiments
const titleExp = statsig.getExperiment(user, "new_user_promo_title");
const priceExp = statsig.getExperiment(user, "new_user_promo_price");
const experimentTitle = titleExp.getValue("title", "Welcome to Statsig!");
const experimentDiscount = priceExp.getValue("discount", 0.1);
// Get values via Layer
const layer = statsig.getLayer(user, "user_promo_experiments");
const title = layer.getValue("title", "Welcome to Statsig!");
const discount = layer.getValue("discount", 0.1);
Retrieving Feature Gate Metadata
In certain scenarios, you may need more information about a gate evaluation than just a boolean value. For additional metadata about the evaluation, use the Get Feature Gate API, which returns a FeatureGate object:
const gate = statsig.getFeatureGate(statsigUser, "example_gate")
console.log(gate.rule_id)
console.log(gate.value)
Parameter Stores
Use Parameter Stores when you want to define a parameter without deciding whether it should be a Feature Gate, Experiment, or Dynamic Config. Parameter Stores let you change the parameter type at any point in the Statsig console without a new deployment. Parameter Stores are optional, but parameterizing your application provides future flexibility and allows non-technical Statsig users to turn parameters into experiments.
const paramStore = statsig.getParameterStore(statsigUser, "my_parameters")
const paramStoreValue = paramStore.getValue('my_parameter_value')
Logging an Event
To track custom events, call the Log Event API. Specify the user, event name, and an optional value or metadata object:
statsig.logEvent(
user,
"add_to_cart",
null,
{
price: "9.99",
item_name: "diet_coke_48_pack"
}
);
Sending Events to Log Explorer
You can forward logs to Logs Explorer for convenient analysis using the Forward Log Line Event API. This lets you include custom metadata and event values with each log.
const user = new StatsigUser({ userID: "a-user", custom: {
service: "my-service",
pod: "my-pod",
namespace: "my-namespace",
container: "my-container",
// ...include any service-specific metadata
} });
// levels: trace, debug, info, log, warn, error
statsig.forwardLogLineEvent(user, "warn", "script failed to load", {
cusom_metadata: "script_name:my-script"
// ... include any event-specific metadata
});
Using shared instance
To create a single Statsig instance accessible globally throughout your codebase, use the shared instance functionality, which provides a singleton pattern:
// Create a shared instance that can be accessed globally
const statsig = Statsig.newShared("secret-key");
await statsig.initialize();
// Access the shared instance from anywhere in your code
const sharedStatsig = Statsig.shared();
const isFeatureEnabled = sharedStatsig.checkGate(user, "feature_name");
// Check if a shared instance exists
if (Statsig.hasSharedInstance()) {
// Use the shared instance
}
// Remove the shared instance when no longer needed
Statsig.removeShared();
The shared instance lets a single Statsig instance be created and accessed globally throughout your application. This is useful when multiple parts of the codebase need Statsig without passing an instance around.
Statsig.newShared(sdkKey, options): Creates a new shared instance of Statsig that can be accessed globallyStatsig.shared(): Returns the shared instanceStatsig.hasSharedInstance(): Checks if a shared instance exists (useful when the shared instance may not be ready yet)Statsig.removeShared(): Removes the shared instance (useful when you want to switch to a new shared instance)
hasSharedInstance() and removeShared() are helpful in specific scenarios but aren't required in most use cases where the shared instance is set up near the top of your application.
Also note that only one shared instance can exist at a time. Attempting to create a second shared instance will result in an error.
Manual exposures
By default, the SDK automatically logs an exposure event when you check a gate, get a config, get an experiment, or call get() on a parameter in a layer. To delay exposure logging (for example, to log only after the user actually uses the feature), use manual exposures.
All main SDK functions (checkGate, getDynamicConfig, getExperiment, getLayer) accept an optional disableExposureLogging parameter. When set to true, the SDK doesn't automatically log an exposure. You can then log the exposure manually at a later time:
const result = statsig.checkGate(aUser, 'a_gate_name', {disableExposureLogging: true});
statsig.manuallyLogGateExposure(aUser, 'a_gate_name');
Statsig User
The StatsigUser object represents a user in Statsig. You must provide a userID or at least one of the customIDs to identify the user.
In addition to userID, the top-level fields on StatsigUser are: email, ip, userAgent, country, locale, and appVersion. You can also pass any key-value pairs in an object or dictionary to the custom field and create targeting based on them.
Private Attributes
Private attributes are user attributes that Statsig uses for evaluation but doesn't forward to any integrations. They are useful for PII or sensitive data that you don't want to send to third-party services.
const user = new StatsigUser({
userID: "a-user-id",
email: "user@example.com",
ip: "192.168.1.1",
userAgent: "Mozilla/5.0...",
country: "US",
locale: "en_US",
appVersion: "1.0.0",
custom: {
// Custom fields
plan: "premium",
age: 25
},
customIDs: {
// Custom ID types
stableID: "stable-id-123"
},
privateAttributes: {
// Private attributes not forwarded to integrations
email: "private@example.com"
}
});
Statsig Options
You can pass an optional options parameter in addition to sdkKey during initialization to customize the Statsig client.
Proxy and Custom Network Routing
UseproxyConfig if your service needs a standard outbound HTTP proxy. Use specAdaptersConfig if you are routing spec downloads through Statsig Forward Proxy or another custom spec source.// Example usage:
const options = new StatsigOptions();
options.environment = "staging";
options.initTimeoutMs = 3000;
options.proxyConfig = {
proxyHost: "proxy.example.com",
proxyPort: 8080,
// proxyAuth: set if authentication is required
proxyProtocol: "https",
caCertPath: "/etc/ssl/certs/corporate-ca.pem"
};
const statsig = new Statsig("secret-key", options);
await statsig.initialize();
Set caCertPath when your environment requires a custom PEM CA bundle for outbound TLS.
Shutting Statsig down
Statsig batches and periodically flushes events. To ensure all logged events are flushed before shutdown, call shutdown() before your app or server shuts down:
await statsig.shutdown();
Client SDK bootstrapping | SSR
If you use the Statsig client SDK in a browser or mobile app, you can bootstrap the client SDK with values from the server SDK to avoid a network request on the client. This is useful for server-side rendering (SSR) or to reduce network requests on the client.
Client initialize response
The Node Core SDK provides a method to generate a client initialize response that can be used to bootstrap client SDKs without requiring network requests.
// Get client initialize response for a user
const values = statsig.getClientInitializeResponse(user, options);
// Pass values to a client SDK to initialize without a network request
SDK Event Subscriptions
The Statsig SDK provides an event subscription system that lets you listen for evaluation events and lifecycle events in real time. This is useful for debugging, analytics, custom logging, and integration with external systems.
Supported Events
The SDK supports subscribing to the following evaluation events:
gate_evaluated- Fired when a feature gate is evaluated for a userdynamic_config_evaluated- Fired when a dynamic config is retrieved for a userexperiment_evaluated- Fired when an experiment is evaluated for a userlayer_evaluated- Fired when a layer is evaluated for a userspecs_updated- Fired when the SDK updates its cached specs, including where the specs were loaded from"*"- Subscribe to all evaluation events
SDK Event Data
Each event includes relevant context about the evaluation:
- Gate Evaluated Events include:
gate_name,value(boolean),rule_id,reason - Dynamic Config Events include: the full
dynamic_configobject with values and metadata - Experiment Events include: the full
experimentobject with variant assignment and parameters - Layer Events include: the full
layerobject with allocated experiment and parameters - Specs Updated Events include
source,source_api, andvaluesmetadata, wherevalues.timeis the timestamp of the last update to the project in the Statsig console
Use Cases
Event subscriptions are particularly useful for:
- Debugging: Monitor which features are being evaluated and their results
- Analytics: Track feature usage patterns and user segments
- Custom Logging: Send evaluation data to your own logging systems
- Integration: Forward events to external analytics or monitoring tools
- Testing: Verify that features are being evaluated as expected
Best Practices
- Clean up subscriptions: Always unsubscribe when you no longer need to listen for events to prevent memory leaks
- Handle event data carefully: Event objects may contain sensitive user information depending on your configuration
- Use specific event types: Subscribe to specific events rather than "*" when possible for better performance
- Avoid heavy processing: Keep event handlers lightweight to avoid impacting SDK performance
const statsig = new Statsig('server-secret-key');
// Subscribe to gate evaluation events
const gateSubId = statsig.subscribe('gate_evaluated', (event) => {
console.log('Gate evaluated:', {
gateName: event.gate_name,
value: event.value,
ruleId: event.rule_id,
reason: event.reason
});
});
// Subscribe to dynamic config evaluation events
const configSubId = statsig.subscribe('dynamic_config_evaluated', (event) => {
console.log('Config evaluated:', {
configName: event.dynamic_config.name,
values: event.dynamic_config.value
});
});
// Subscribe to experiment evaluation events
const experimentSubId = statsig.subscribe('experiment_evaluated', (event) => {
console.log('Experiment evaluated:', {
experimentName: event.experiment.name,
groupName: event.experiment.group_name,
parameters: event.experiment.value
});
});
// Subscribe to layer evaluation events
const layerSubId = statsig.subscribe('layer_evaluated', (event) => {
console.log('Layer evaluated:', {
layerName: event.layer.name,
allocatedExperiment: event.layer.allocated_experiment_name,
parameters: event.layer.value
});
});
// Subscribe to specs updated events
const specsUpdatedSubId = statsig.subscribe('specs_updated', (event) => {
console.log('Specs updated:', {
source: event.data.source,
sourceApi: event.data.source_api,
projectLastUpdated: event.data.values.time,
});
});
// Subscribe to all events
const allEventsSubId = statsig.subscribe('*', (event) => {
console.log('Event received:', event.event_name, event);
});
// Unsubscribe from specific event types
statsig.unsubscribe('gate_evaluated');
statsig.unsubscribe('specs_updated');
// Unsubscribe using subscription ID
statsig.unsubscribeById(configSubId);
// Unsubscribe from all events
statsig.unsubscribeAll();
Local overrides
Local Overrides let you override the values of gates, configs, experiments, and layers for testing. This is useful for local development or testing when you want to force a specific value without changing the configuration in the Statsig console.
// Overrides the given gate to the specified value
Statsig.overrideGate("a_gate_name", true);
// Optional third parameter, overrides the gate only for a given ID
Statsig.overrideGate("a_gate_name", true, "userID-123");
Persistent storage
The Persistent Storage interface lets you implement custom storage for user-specific configurations. This enables you to persist user assignments across sessions, ensuring consistent experiment groups when a user returns. This is useful for client-side A/B testing where users must always receive the same variant.
export interface PersistentStorage {
load: (key: string) => UserPersistedValues | null;
save: (key: string, config_name: string, data: StickyValues) => void;
delete: (key: string, config_name: string) => void;
}
export interface StickyValues {
value: boolean;
json_value: Record<string, unknown>;
rule_id: string;
group_name: string | null;
secondary_exposures: SecondaryExposure[];
undelegated_secondary_exposures: SecondaryExposure[];
config_delegate: string | null;
explicit_parameters: string[] | null;
time: number;
configVersion?: number | undefined;
}
export type UserPersistedValues = Record<string, StickyValues>;
export interface SecondaryExposure {
gate: string;
gateValue: string;
ruleId: string;
}
Usage Example
import { PersistentStorage, StickyValues, UserPersistedValues } from '@statsig/statsig-node-core';
class MyPersistentStorage implements PersistentStorage {
private storage = new Map<string, UserPersistedValues>();
constructor() {
this.load = this.load.bind(this);
this.save = this.save.bind(this);
this.delete = this.delete.bind(this);
}
load(key: string): UserPersistedValues | null {
return this.storage.get(key) || null;
}
save(key: string, config_name: string, data: StickyValues): void {
const existing = this.storage.get(key) || {};
existing[config_name] = data;
this.storage.set(key, existing);
}
delete(key: string, config_name: string): void {
const existing = this.storage.get(key);
if (existing) {
delete existing[config_name];
this.storage.set(key, existing);
}
}
getUserPersistedValue(user: StatsigUser, idType: string): UserPersistedValues | null {
const storageKey = this.getStorageKey(user, idType);
if (storageKey !== null) {
return this.load(storageKey);
}
return null;
}
private getStorageKey(user: StatsigUser, idType: string): string | null {
const lowerCaseIdType = idType.toLowerCase();
if (lowerCaseIdType === "user_id" || lowerCaseIdType === "userid") {
const id = user.userID;
return id ? `${id}:userID` : null;
} else if (user.customIDs) {
const id = user.customIDs[idType];
return id ? `$\{id\}:${idType}` : null;
}
return null;
}
}
Persistent storage support was added in version 0.6.1 of the Node.js SDK.
Data store
The Data Store interface lets you implement custom storage for Statsig configurations, enabling advanced caching strategies and integration with your preferred storage systems.
export interface DataStore {
initialize?: () => Promise<void>;
shutdown?: () => Promise<void>;
get?: (key: string) => Promise<DataStoreResponse>;
set?: (key: string, value: string, time?: number) => Promise<void>;
supportPollingUpdatesFor?: (key: string) => Promise<boolean>;
}
export interface DataStoreResponse {
result?: string;
time?: number;
}
Custom output logger
The Output Logger interface lets you customize how the SDK logs messages, enabling integration with your own logging system and control over log verbosity.
Observability client
The Observability Client interface lets you monitor the health of the SDK by integrating with your own observability systems. This enables you to track metrics, errors, and performance data. For more information on the metrics Statsig SDKs emit, refer to the Monitoring documentation.export interface ObservabilityClient {
initialize?: () => void;
increment?: (metricName: string, value: number, tags: Record<string, string>) => void;
gauge?: (metricName: string, value: number, tags: Record<string, string>) => void;
dist?: (metricName: string, value: number, tags: Record<string, string>) => void;
error?: (tag: string, error: string) => void;
}
FAQs
How do I run experiments for logged out users?
Common Problems while installing
- Seeing SSL Error
The binary files require certain SSL versions.
// Try run this
apt-get update && apt-get install libcurl4-openssl-dev -y && rm -rf /var/lib/apt/lists/*
- Docker build failing with platform-specific dependencies
When building in Docker (Linux environment), the build may fail if your local package-lock.json or yarn.lock contains platform-specific dependencies for macOS. This happens because npm install on Mac pulls down Apple-specific variants, but Docker tries to use those locked dependencies on Linux.
Solution: Either install the Linux-specific variant during your Docker build step:
RUN npm install @statsig/statsig-node-core-linux-x64-gnu
Or add both platform variants as dependencies in your package.json:
"dependencies": {
"@statsig/statsig-node-core": "X.Y.Z", // Common (Required)
"@statsig/statsig-node-core-darwin-arm64": "X.Y.Z", // Mac Specific
"@statsig/statsig-node-core-linux-x64-gnu": "X.Y.Z" // Linux Specific
}
Reference
Was this helpful?