Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/statsig-node-core-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@flags-sdk/statsig": major
---

Migrate to `@statsig/statsig-node-core` (Statsig's Rust-based server SDK).

This is a breaking change. The underlying Statsig SDK is now instance-based instead of a singleton, and several method names and option keys have changed.

**Breaking changes**

- The exported `Statsig` is now a class (`new Statsig(key, options)`), not a singleton. Methods such as `Statsig.getFeatureGateSync` no longer exist — use the instance returned by `statsigAdapter.initialize()` and call `getFeatureGate(user, key)` etc.
- Sync method variants (`*Sync`) and `*WithExposureLoggingDisabledSync` are removed. Pass `{ disableExposureLogging: true }` as the third argument instead.
- The `DynamicConfig` and `Experiment` types are now distinct (the adapter's `experiment` getter receives an `Experiment`).
- `statsigOptions` keys changed: use `specsSyncIntervalMs` (was `rulesetsSyncIntervalMs`), `enableIdLists` (was `disableIdListsSync`/`initStrategyForIDLists`), and `dataStore` (was `dataAdapter`).
- `getClientInitializeResponse` now returns a JSON `string` and accepts `{ hashAlgorithm: 'djb2' }` instead of `{ hash: 'djb2' }`.
- The Edge Runtime workaround hooks were removed. The new SDK uses native Node bindings (NAPI) and runs on Node.js only — including Vercel's Fluid Compute. It is not compatible with the Edge Runtime.

**Internal changes**

- Drops the `statsig-node-vercel` dependency. The Edge Config integration is now implemented inline using a custom `DataStore` that reads from `@vercel/edge-config`.
54 changes: 28 additions & 26 deletions apps/docs/content/docs/providers/statsig.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ When using server-side rendering the server can inline the information needed to
Your application roughly needs to follow these steps:

1. Call the same `identify` function your feature flags use to get the Statsig user.
2. Call `statsigAdapter.initialize()` to initialize the `statsig-node-lite` SDK.
2. Call `statsigAdapter.initialize()` to initialize the `@statsig/statsig-node-core` SDK.
3. Prepare the bootstrap data on the server, and pass it to the client.
4. Use the bootstrap data on the browser to initialize a client and set up the Statsig React provider.

Expand All @@ -232,7 +232,7 @@ Below are the most critical pieces you need to implement this, which is meant as

```tsx title="app/(example)/layout.tsx#next"
import { cookies, headers } from "next/headers";
import { statsigAdapter } from "@flags-sdk/statsig";
import { statsigAdapter, StatsigUser } from "@flags-sdk/statsig";
import { DynamicStatsigProvider } from "./dynamic-statsig-provider";
// The same identify function you use when declaring flags
// See https://flags-sdk.dev/docs/api-reference/adapters/statsig#identify-users
Expand All @@ -247,11 +247,12 @@ export default async function Layout({
const user = await identify({ headers: headersStore, cookies: cookieStore });

// Get a reference to the Statsig SDK instance configured by the adapter
const Statsig = await statsigAdapter.initialize();
const statsig = await statsigAdapter.initialize();

// Prepare the bootstrap data on the server, and pass it to the client
const datafile = await Statsig.getClientInitializeResponse(user, {
hash: "djb2", // must use this hash function for compatibility with the client
// Prepare the bootstrap data on the server, and pass it to the client.
// The core SDK returns a JSON string ready to hand to the browser client.
const datafile = statsig.getClientInitializeResponse(new StatsigUser(user), {
hashAlgorithm: "djb2", // must use this hash function for compatibility with the client
});

return (
Expand All @@ -268,7 +269,6 @@ export default async function Layout({
"use client";

import { useMemo } from "react";
import type { Statsig } from "@flags-sdk/statsig";
import {
StatsigProvider,
useClientBootstrapInit,
Expand All @@ -279,23 +279,26 @@ export function DynamicStatsigProvider({
datafile,
}: {
children: React.ReactNode;
datafile: Awaited<ReturnType<typeof Statsig.getClientInitializeResponse>>;
datafile: string;
}) {
if (!datafile) throw new Error("Missing datafile");

// Statsig expects a stringified datafile, but ideally the Statsig SDK
// would accept a JSON object so we could avoid this stringification.
const datafileString = useMemo(() => JSON.stringify(datafile), [datafile]);
// The server returns the datafile as a JSON string already, but we still need
// to parse out the user to pass it to the StatsigProvider.
const parsed = useMemo(
() => JSON.parse(datafile) as { user: Record<string, unknown> },
[datafile],
);

const client = useClientBootstrapInit(
process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY as string,
datafile.user,
datafileString
parsed.user,
datafile,
// NOTE you could provide the Autocapture plugin here
);

return (
<StatsigProvider user={datafile.user} client={client}>
<StatsigProvider user={parsed.user} client={client}>
{children}
</StatsigProvider>
);
Expand Down Expand Up @@ -371,21 +374,20 @@ The default Statsig adapter, exported as `statsigAdapter`, will automatically co

The Flags SDK automatically initializes the Statsig client when a flag is evaluated.

If you want to initialize the Statsig client before the first flag is used, you can call `statsigAdapter.initialize` manually. Further,
use the manual call to initialize `statsig-node-lite` for usage with other server-side code.
If you want to initialize the Statsig client before the first flag is used, you can call `statsigAdapter.initialize` manually. The returned `Statsig` instance is the same one the adapter uses internally, so you can reuse it for other server-side code.

```ts
import { statsigAdapter, Statsig } from '@flags-sdk/statsig';
import { statsigAdapter, type StatsigUser } from '@flags-sdk/statsig';

const statsigInitializationPromise = statsigAdapter.initialize();
const statsigPromise = statsigAdapter.initialize();

export async function getStatsigExperiment(key: string) {
await statsigInitializationPromise;
return Statsig.getExperimentSync(key);
export async function getStatsigExperiment(user: StatsigUser, key: string) {
const statsig = await statsigPromise;
return statsig.getExperiment(user, key);
}
```

Use `statsigAdapter.initialize` instead of `Statsig.initialize` as it configures the Statsig client specifically for Flags SDK compatibility.
Use `statsigAdapter.initialize` instead of `new Statsig(...)` directly, as it configures the Statsig client specifically for Flags SDK compatibility (including Edge Config bootstrapping when configured).


### Same key with different mapping functions
Expand Down Expand Up @@ -416,9 +418,9 @@ export const myDynamicPrice = flag<number, StatsigUser>({
});
```

### Statsig Node Lite
### Statsig Node Core

The adapter uses `statsig-node-lite`, which is a slimmed version of the Statsig Node.js SDK optimized for server side and Routing Middleware usage.
The adapter uses [`@statsig/statsig-node-core`](https://www.npmjs.com/package/@statsig/statsig-node-core), Statsig's Rust-based server SDK with Node.js bindings. It runs on Node.js (including Vercel's Fluid Compute) and is not compatible with the Edge Runtime.

### Exposure Logging

Expand All @@ -434,7 +436,7 @@ export const exampleFlag = flag<boolean, StatsigUser>({
});
```

When logging is on, your application should also call `Statsig.flush` appropriately to ensure exposures are recorded.
When logging is on, your application should also call `statsig.flushEvents()` (using the instance returned from `statsigAdapter.initialize()`) appropriately to ensure exposures are recorded.

The recommended approach for experimentation is to log exposures from the client when
the user is indeed exposed to an experiment, either when seen or interacted with.
Expand Down Expand Up @@ -482,4 +484,4 @@ Read more about Statsig, Flags SDK, and the Statsig adapter.
- [Adapter Concept](/docs/adapters/supported-providers)
- [Precompute Concept](/principles/precompute)
- [Statsig with Next.js](https://docs.statsig.com/client/javascript-sdk/next-js/)
- [Statsig Node Lite on NPM](https://www.npmjs.com/package/statsig-node-lite)
- [Statsig Node Core on NPM](https://www.npmjs.com/package/@statsig/statsig-node-core)
6 changes: 2 additions & 4 deletions packages/adapter-statsig/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,8 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@vercel/edge-config": "^1.4.3",
"@vercel/functions": "^1.5.2",
"statsig-node-lite": "^0.5.2",
"statsig-node-vercel": "^0.7.0"
"@statsig/statsig-node-core": "^0.19.3",
"@vercel/edge-config": "^1.4.3"
},
"devDependencies": {
"@types/node": "20.11.17",
Expand Down
73 changes: 0 additions & 73 deletions packages/adapter-statsig/src/edge-runtime-hooks.ts

This file was deleted.

Loading
Loading