Your app reads flags with one call: evaluate. It answers for the signed-in user and their tenant, with the tenant's changes applied, and says when to ask again. Copy-paste clients for the main frameworks are in the repository under docs/samples/feature-flags-client; they have no dependencies and are small enough to read.
The call
curl -X POST "$BASE/app/$ORG/featureflags/evaluate" \
-H "x-nostackai-token: $TOKEN" -H "content-type: application/json" \
-d '{"keys":["new-checkout"],"context":{"attributes":{"plan":"pro"}}}' {
"flags": {
"new-checkout": { "value": true, "variation": "on", "reason": "RULE_MATCH" }
},
"next_change_at": null,
"etag": "6b1f0c2a9d4e7f31"
} | Field | Meaning |
|---|---|
value | What your app uses. |
reason | Why: OFF, TARGET_MATCH, RULE_MATCH, FALLTHROUGH, or ERROR (treat as missing). |
next_change_at | When a scheduled change or window is next due, or null. Ask again then. |
etag | Changes whenever any flag changes; handy to see if anything is new. |
The user is always the one in the token — your app cannot ask on behalf of someone else. keys is optional and narrows the answer. attributes are facts your rules can use. GET .../featureflags/evaluate?keys=a,b answers the same for a user with read-only access (no attributes).
Plain JavaScript
import { FlagsClient } from './flagsClient';
const flags = new FlagsClient({
baseUrl: 'https://<your-app-domain>',
orgCode: 'acme',
getToken: () => session.token, // the signed-in user's app token
attributes: { plan: 'pro' }, // optional facts for your rules
});
await flags.start(); // never throws; you get defaults if it fails
if (flags.variation('new-checkout', false)) {
showNewCheckout();
}
const unsubscribe = flags.onChange(() => rerender()); React
// once, near the root
<FlagsProvider client={flags}><App /></FlagsProvider>
// anywhere
function Checkout() {
const newCheckout = useFeatureFlag('new-checkout', false);
return newCheckout ? <NewCheckout /> : <OldCheckout />;
} Vue 3
// main.ts
app.provide(FLAGS_KEY, flags);
// a component
const newCheckout = useFeatureFlag('new-checkout', false); // a ref
// <NewCheckout v-if="newCheckout" /> Angular
// app.config.ts
providers: [{ provide: FLAGS_CLIENT, useValue: flags }, FeatureFlagsService]
// a component
readonly newCheckout = this.flags.flag('new-checkout', false); // a Signal<boolean>
// @if (newCheckout()) { <app-new-checkout /> }
// or in a template
// <app-new-checkout *ifFlag="'new-checkout'; else: oldCheckout"></app-new-checkout> From your own server
Call the evaluate endpoint from your backend with the signed-in user's token and pass the answer on, or hand your frontend the answer. Server code that runs inside nostackai (Logic Blocks, Glyphs, agents) does not need to call anything — see In Logic Blocks, Glyphs and agents.
Rules of thumb
- Always pass a default. If flags cannot be loaded the default is used and your app keeps working.
- Read flags once per page/session (the client does) and let
onChangeupdate the screen; don't call evaluate on every render. - Send only facts you need as
attributes; they are used for the answer and not stored. - Keep the token fresh: the client calls
getTokenbefore every request. - Remove a flag from your code and then delete it once a feature is fully live.