Reacting to changes
onChange fires after a background poll picks up new rules — use it to invalidate caches or re-render when a flag flips.
Reads are synchronous against an in-memory rule set that a background poll keeps fresh. onChange lets you react the moment that rule set swaps — for cache busting, re-rendering, or logging a config flip.
Change listeners live on the Engine — the heavyweight client. Everyday flag reads use configure + new Client(user); here you hold the Engine itself so you can subscribe to its poll cycle.
Server
The server Engine fires registered listeners after a background poll returns new data (HTTP 200, not a 304). It returns an unsubscribe function, and never fires in test or offline mode (no polling happens there).
import { Engine } from "@shipeasy/sdk/server";
const engine = new Engine({ apiKey: process.env.SHIPEASY_SERVER_KEY! });
await engine.init();
const unsubscribe = engine.onChange(() => {
console.log("flag rules changed — re-evaluating");
rebuildRenderCache();
});
// on shutdown
unsubscribe();The poll sends an ETag; an unchanged body comes back 304 and listeners
stay quiet. You only wake up when something actually changed in the dashboard.
Browser
The browser Engine exposes the equivalent subscribe(), which fires after each identify() and after any override change — exactly when a re-render is warranted.
import { Engine } from "@shipeasy/sdk/client";
const unsubscribe = engine.subscribe(() => {
renderFlags();
});When to use it
- Cache invalidation — drop a memoized render or a derived config when rules change.
- Long-lived servers — re-evaluate a flag that gates a background job without restarting.
- Dashboards — reflect a flag flip live without a page reload.
For one-shot serverless reads there is nothing to subscribe to — see the TypeScript configuration reference.
Evaluation reasons
getFlagDetail returns the value plus the reason a flag resolved the way it did — RULE_MATCH, DEFAULT, OFF, OVERRIDE, FLAG_NOT_FOUND, and CLIENT_NOT_READY.
Multi-context bucketing (bucketBy)
Bucket on company_id, session_id, or any attribute instead of user_id — so a rollout splits by account, not by individual user.