Node.js SDK
The same @growthbook/growthbook package that runs in the browser also works in Node.js, so there is no separate server SDK to install. The key difference is the lifecycle model: on the server, flag evaluation happens inside request handlers rather than once at app startup.
Server-side evaluation
Section titled “Server-side evaluation”In a browser app you typically create one GrowthBook instance for the lifetime of the page. On the server, each HTTP request may represent a completely different user, so you need per-request scoping — create (or clone) a fresh instance for every request and set user-specific attributes on it.
Caching the feature payload
Section titled “Caching the feature payload”Fetching feature definitions from GrowthBook on every request would add latency and burn bandwidth. Instead, fetch the payload once at startup, store it in memory, then pass it to each per-request instance via gb.init({ payload: cachedPayload }). This skips the network call entirely while keeping flag evaluation accurate.
You can retrieve the payload from the loader instance with gb.getPayload() and later hand it to a fresh instance with gb.setPayload(payload) or by passing it directly to init.
Why per-request scoping matters
Section titled “Why per-request scoping matters”If you shared a single GrowthBook instance across requests and called gb.setAttributes(...) in each handler, concurrent requests would overwrite each other’s user context — leading to incorrect flag evaluations and hard-to-reproduce bugs. A fresh instance per request eliminates this race condition entirely.
import { GrowthBook, setPolyfills } from '@growthbook/growthbook';
// --- App startup: fetch and cache the payload once ---
let cachedPayload = null;
const loader = new GrowthBook({
apiHost: 'https://cdn.growthbook.io',
clientKey: 'sdk-abc123',
});
await loader.init({ timeout: 3000 });
cachedPayload = loader.getPayload();
// --- Per-request handler ---
async function handleRequest(req, res) {
// Fresh instance per request — no shared mutable state
const gb = new GrowthBook({
apiHost: 'https://cdn.growthbook.io',
clientKey: 'sdk-abc123',
});
// Reuse the cached payload — no extra network call
await gb.init({ payload: cachedPayload });
// Scope attributes to this request's user
gb.setAttributes({
id: req.user.id,
country: req.headers['cf-ipcountry'] ?? 'US',
});
const useBetaUI = gb.isOn('beta-ui');
res.json({ useBetaUI });
gb.destroy(); // free listeners
}