Flags guides
03 · OpenFeature

Use the OpenFeature API.

Keep your code on the vendor-neutral OpenFeature server SDK and let Flags evaluate underneath.


1

Install and register the provider

terminal
npm install @openfeature/server-sdk @productcraft/flags @productcraft/flags-openfeature

The provider takes the same options as FlagsClient (polling, cache, bootstrap, overrides) and evaluates locally, like the plain SDK.

flags.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { ProductCraftFlagsProvider } from "@productcraft/flags-openfeature";

// Takes FlagsClient options (polling, cache, bootstrap, overrides …).
await OpenFeature.setProviderAndWait(
  new ProductCraftFlagsProvider({ sdkKey: process.env.PRODUCTCRAFT_FLAGS_SDK_KEY }),
);

const client = OpenFeature.getClient();
const context = { targetingKey: "user-123", email: "ada@example.com", plan: "pro" };

const enabled = await client.getBooleanValue("new_checkout", false, context);
const banner = await client.getStringValue("banner_text", "Welcome", context);
const limits = await client.getObjectValue("limits", { max: 5 }, context);

const details = await client.getBooleanDetails("new_checkout", false, context);
console.log(enabled, banner, limits, details.reason, details.variant, details.flagMetadata);

await OpenFeature.close(); // closes the FlagsClient underneath

2

How the context maps

OpenFeature to Flags mapping
OpenFeatureFlags
targetingKeyidentifier (the attribute percentage splits hash)
email, countrythe top-level user fields
other strings, numbers, booleans, Dates, string[]custom attributes rules can target
nested objects, mixed arraysdropped (logged at debug level)
object resolvera json setting, parsed
reasonSTATIC, TARGETING_MATCH, SPLIT, DEFAULT, CACHED, STALE, ERROR
variantrule:<i>, rule:<i>/pct:<j>, pct:<j> or default
flagMetadatarevision, plus rule_index / percentage_index
errorCodeFLAG_NOT_FOUND, TYPE_MISMATCH, PARSE_ERROR, PROVIDER_NOT_READY; prerequisite errors are GENERAL

3

Lifecycle and events

  • Initialisation waits for the first download, at most polling.maxInitWaitSeconds (5 by default). It fails only when there is no config at all: no download, no cache, no bootstrap.
  • PROVIDER_CONFIGURATION_CHANGED fires when a new revision is adopted, with the changed keys.
  • PROVIDER_STALE when a download fails while an older config is served; PROVIDER_ERROR when the SDK key is unknown, revoked or expired (the held config is still served).
  • Evaluations never throw: on any failure you get your default with reason ERROR.

4

In tests

checkout.test.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createTestClient } from "@productcraft/flags/testing";
import { ProductCraftFlagsProvider } from "@productcraft/flags-openfeature";

const client = createTestClient({ values: { new_checkout: true } });
await OpenFeature.setProviderAndWait(new ProductCraftFlagsProvider({ client }));

client.set("new_checkout", false); // emits PROVIDER_CONFIGURATION_CHANGED