We officially support Node 18 and above. Node SDK Resources v1.6.5 sdk-jsnpmExpress.js guideGet help on SlackDocumentation Index
Fetch the complete documentation index at: https://growthbook-preview.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Installation
Install with a package manager- npm
- Yarn
- pnpm
- Bun
Quick Usage
First, create and instantiate a singleton GrowthBook client. You can do this as part of your server startup script.userContext.
Express Middleware
In Express, it can be helpful to add a user-scoped GrowthBook instance. This will let you define auserContext object once instead of passing it into every feature evaluation call throughout your app.
This can be accomplished easily with a middleware.
userContext again.
Plugins
GrowthBook comes with a number of built-in plugins that add additional functionality. You can enable these by passing them into the GrowthBook client’screateScopedInstance() factory method or into the GrowthBook constructor.
Plugins require GrowthBook version 1.4.0 or higher, released in Feb 2025.
DevTools for back-end
The GrowthBook DevTools Browser Extension now supports debugging back-end environments. This includes sending overrides (attribute, feature, and experiment) from the browser extension to the back end, and also displaying back-end logged events (feature evaluations, experiment exposure events). We provide first-class devtools support via plugins and helper functions for a variety of Node.js-based environments, including:- Next.js
- Express
- Node.js (platform-agnostic)
GrowthBook class (legacy)
TheGrowthBookClient class was introduced in SDK version 1.3.0 to improve performance in back-end environments by up to 3x. It does this by re-using the core instance across many different user requests.
For backwards compatibility, we still support the old way of doing things with the GrowthBook class, where you would create a new instance for every incoming request.
GrowthBook class is identical to the one used in client-side environments. Check out the Client-side JavaScript SDK docs for more info on all of the settings and methods available.
Loading Features and Experiments
In order for the GrowthBook SDK to work, it needs to have feature and experiment definitions from the GrowthBook API. There are a few ways to get this data into the SDK.Built-in Fetching and Caching
If you pass anapiHost and clientKey into the GrowthBookClient constructor, it will handle the network requests, caching, retry logic, etc. for you automatically.
Error Handling
In the case of network issues that prevent the features from downloading in time, theinit call will not throw an error. Instead, it will stay in the default state where every feature evaluates to null.
You can still get access to the error if needed:
- success -
trueif the GrowthBook instance was populated with features/experiments. Otherwisefalse - source - Where this result came from. One of the following values:
network,cache,init,error, ortimeout - error - If success is
false, this will contain anErrorobject with more details about the error
Custom Integration
If you prefer to handle the network and caching logic yourself, you can pass in a full JSON “payload” directly into the SDK. For example, you might store features in Postgres or Redis.clientKey or apiHost on your GrowthBook instance since no network requests are being made in this case.
Synchronous Init
There is a alternate synchronous version of init namedinitSync, which can be useful in some environments. There are some restrictions/differences:
- You MUST pass in
payload - The
payloadMUST NOT have encrypted features or experiments - The return value is the GrowthBook instance to enable easy method chaining
- If using sticky bucketing, you should use an instance of
StickyBucketServiceSync, such asExpressCookieStickyBucketService.
Refreshing Features
By default, the GrowthBookClient will only fetch features once during initialization. This works great for short-running processes (e.g. serverless functions). For long-running Node.js processes, there are 2 main approaches to keeping feature defintiions up-to-date.- Streaming
- Polling
Streaming Updates
The GrowthBook SDK supports streaming with Server-Sent Events (SSE). When enabled, changes to features within GrowthBook will be streamed to the SDK in realtime as they are published. This is only supported on GrowthBook Cloud or if running a GrowthBook Proxy Server. Node.js does not natively support SSE, but there is a small library you can install:- npm
- Yarn
- pnpm
- Bun
Polling Updates
If your environment doesn’t support streaming updates or you want a simpler option, you can use a polling approach:Caching
The JavaScript SDK has 2 caching layers:- In-memory cache (enabled by default)
- Persistent localStorage cache (disabled by default, requires configuration)
Configuring Local Storage
Here is an example of using Redis as your persistent localStorage cache:Cache Settings
There are a number of cache settings you can configure within GrowthBook. Below are all of the default values. You can callconfigureCache with a subset of these fields and the rest will keep their default values.
Skip Cache
The cache layers apply to both theinit and refreshFeatures methods. Both of these accept a skipCache: true option to bypass the cache layers if desired.
Experimentation (A/B Testing)
In order to run A/B tests, you need to set up a tracking callback function. This is called every time a user is put into an experiment and can be used to track the exposure event in your analytics system (Segment, Mixpanel, GA, etc.). You can specify this globally in your GrowthBookClient class and/or per-request in your user context. We recommend using a global callback if you plan to track events directly from Node and using a user context callback if you plan to send events to the front-end to fire. When specified globally, the callbacks will receive an additional argument with the user context that triggered the event.Feature Flag Experiments
There is nothing special you have to do for feature flag experiments. Just evaluate the feature flag like you would normally do. If the user is put into an experiment as part of the feature flag, it will call thetrackingCallback automatically in the background.
result.featureId in the trackingCallback will contain the feature id, which may be useful for tracking/logging purposes.
Deferred Tracking
Sometimes, you aren’t able to track analytics events from Node.js and you need to do it from the front-end instead. In this case, define a trackingCallback in the user context, queue up events, and serialize it in your response. Here’s an example:trackingCallback configured on your front-end GrowthBook instance.
Sticky Bucketing
Sticky bucketing ensures that users see the same experiment variant, even when user session, user login status, or experiment parameters change. See the Sticky Bucketing docs for more information. If your organization and experiment supports sticky bucketing, you must implement an instance of theStickyBucketService to use Sticky Bucketing. The JS SDK exports several implementations of this service for common use cases, or you may build your own:
ExpressCookieStickyBucketService— For NodeJS/Express controller-level bucket persistence using browser cookies; intended to be interoperable withBrowserCookieStickyBucketService. Assumescookie-parseris implemented (can be polyfilled). Cookie attributes can also be configured. The default cookie expiry is 180 days; override by passingmaxAge: {ms}into the constructor’scookieAttributes.RedisStickyBucketService— For NodeJS Redis-based bucket persistence. Requires anioredisRedis client instance to be passed in.- Build your own — Implement the abstract
StickyBucketServiceclass and connect to your own data store, or custom wrap multiple service implementations (ex: read/write to both cookies and Redis).
ExpressCookieStickyBucketService:
TypeScript
When used in a TypeScript project, GrowthBook includes basic type inference out of the box:Strict Typing
If you want to enforce stricter types in your application, you can do that when creating the GrowthBook instance:AppFeatures interface manually like above, you can auto-generate it from your GrowthBook account using the GrowthBook CLI.
Updating
As a general philosophy, we aim to keep the SDK 100% backwards compatible at all times. View the Changelog for a complete list of all SDK changes.GrowthBook Instance (reference)
Attributes
You can specify attributes about the current user and request. These are used for two things:- Feature targeting (e.g. paid users get one value, free users get another)
- Assigning persistent variations in A/B tests (e.g. user id “123” always gets variation B)
Global Attributes
Sometimes there are global attributes that apply to all users. For example, the ip of your server. These can be specified on the GrowthBookClient instance and will be merged with any attributes in the user context.Secure Attributes
When secure attribute hashing is enabled, all targeting conditions in the SDK payload referencing attributes with datatypesecureString or secureString[] will be anonymized via SHA-256 hashing. This allows you to safely target users based on sensitive attributes. You must enable this feature in your SDK Connection for it to take effect.
If your SDK Connection has secure attribute hashing enabled, you will need to manually hash any secureString or secureString[] attributes that you pass into the GrowthBook SDK.
To hash an attribute, use a cryptographic library with SHA-256 support, and compute the SHA-256 hashed value of your attribute plus your organization’s secure attribute salt.
First, define a sha256 function using Node’s built-in crypto support.
Feature Usage Callback
GrowthBook can fire a callback whenever a feature is evaluated for a user. This can be useful to update 3rd party tools like NewRelic or DataDog. Like with thetrackingCallback, this can be defined on either the GrowthBookClient instance or as part of the user context. When defined on the GrowthBookClient instance, a 3rd argument with the user context will be sent so you can identify which user evaluated a feature.
result argument is the same thing returned from evalFeature.
Note: If you evaluate the same feature multiple times (and the value doesn’t change), the callback will only be fired the first time.
evalFeature
In addition to theisOn and getFeatureValue helper methods, there is the evalFeature method that gives you more detailed information about why the value was assigned to the user.
Inline Experiments
Instead of declaring all features up-front in the payload and referencing them by ids in your code, you can also just run an experiment directly. This is done with therunInlineExperiment method:
Customizing the Traffic Split
By default, this will include all traffic and do an even split between all variations. There are 2 ways to customize this behavior:Hashing
We use deterministic hashing to assign a variation to a user. We hash together the user’s id and experiment key, which produces a number between0 and 1. Each variation is assigned a range of numbers, and whichever one the user’s hash value falls into will be assigned.
You can customize this hashing behavior:
hashVersion is specified, it will fall back to using version 1, which is deprecated. In the future, version 2 will become the default. We recommend specifying version 2 now for all new experiments to avoid migration issues down the line.
Meta Info
You can also define meta info for the experiment and/or variations. These do not affect the behavior, but they are passed through to thetrackingCallback, so they can be used to annotate events.
Mutual Exclusion
Sometimes you want to run multiple conflicting experiments at the same time. You can use thefilters setting to run mutually exclusive experiments.
We do this using deterministic hashing to assign users a value between 0 and 1 for each filter.
Holdout Groups
To use global holdout groups, use a nested experiment design:Targeting Conditions
You can also define targeting conditions that limit which users are included in the experiment. These conditions are evaluated against theattributes passed into the user context. The syntax for conditions is based on the MongoDB query syntax and is straightforward to read and write.
For example, if the attributes are:
true and the user would be included in the experiment:
Inline Experiment Return Value
A call torunInlineExperiment returns an object with a few useful properties:
inExperiment flag will be false if the user was excluded from being part of the experiment for any reason (e.g. failed targeting conditions).
The hashUsed flag will only be true if the user was randomly assigned a variation. If the user was forced into a specific variation instead, this flag will be false.
Implementing DevTools
The DevTools plugins are designed to work both GrowthBookClient and GrowthBook (legacy) SDK instances. Your SDK must be in dev mode (enableDevMode: true) for the plugin to work.
Next.js
RSCs and SSR
For RSCs (React server components) or SSR (server-side rendering), in order to apply SDK overrides from DevTools, pass the readonly request cookies and search params into thedevtoolsNextjsPlugin plugin. In Next 15+, these may need to be awaited.
<DevtoolsLogger> which is used to hydrate SDK information and log events back into the DevTools extension. When using this component, DevTools will automatically ingest any data generated by the getDebugScriptContents() method. You may implement it like this:
Middleware and API routes
For middleware implementations or API routes, pass therequest object to devtoolsNextjsPlugin instead of searchParams and requestCookies. A middleware example is below; it can be modified to be an API route:
x-debug-events header value on the front-end and pushing it to window._gbdebugEvents. You may wish to import the getDebugScriptContents() method on the front-end to ingest these debug events. An example integration may look like this:
Express
In an Express.js context, in order to apply SDK overrides from DevTools, pass the request object into thedevtoolsExpressPlugin plugin. You must use the standard Express cookie-parser middleware (npm: cookie-parser) in order to use this plugin.
x-debug-events header value on the front-end and pushing it to window._gbdebugEvents. You may use getDebugScriptContents() on the front-end to help.
Node.js (platform agnostic)
We provide a simpledevtoolsPlugin for other Node.js back-end implementations. This plugin is a stub for the other platform-specific plugins (Next.js, Express); however, it may be used directly.
When using this plugin, you are responsible for building the devtoolsState object by parsing the _gbdebug cookie and the _gbdebug URL query parameter. devtoolsState represents the overrides applied to attributes, features, and experiments; it takes the form:
_gbdebugEvents payload using getDebugEvent().

