Register the provider
CallsetProviderAndWait once at application startup. It awaits the provider’s initialize, which performs the first /v1/sync call and opens the event stream before the promise resolves, so the first evaluation reads real values rather than defaults.
setProviderAndWait rejects if the service cannot be read. A provider that cannot read the service is not ready, and reporting otherwise would serve every caller’s default as though it were an answer. Which error arrives says whether it is worth trying again.
A failure only a change to what you passed in can fix - a rejected API key, a base URL naming no service, a configuration value the client will not accept - arrives as OpenFeature’s own ProviderFatalError, carrying error code PROVIDER_FATAL. The client goes to FATAL, and the SDK stops calling this provider’s resolvers, so every evaluation returns your default at once rather than through a client that will never be there. The underlying Nexus error is on cause.
Anything else - a timed-out request, an unreachable network, a server error, an overdue invoice - arrives as a NexusInitError with the reason on cause. The client goes to ERROR, and registering the provider again can clear it.
instanceof answers from a brand rather than from the prototype chain, so it holds for an error raised by any copy of these classes: this package ships an ES module and a CommonJS build, and the Angular, React and Vue SDKs carry copies of their own.
Evaluating flags
*Details variants to see why a value was resolved:
Per-user targeting
Set an evaluation context once the user is known, and every boolean evaluation afterwards is answered for that user - AB tests, cohort rules and percentage rollouts apply.A targeting-key change is asynchronous:
resolveBooleanEvaluation is synchronous in the OpenFeature Web SDK, so the provider fetches when the context changes rather than during an evaluation. Await setContext.reason means, and how it behaves when the service is unreachable.
Reacting to live updates
The provider holds an event stream open. When a flag or config actually changes it re-syncs, re-asks the targeted answers, and emitsConfigurationChanged. A stream reconnect on its own does not emit it - nothing about the configuration moved.
Type resolution rules
Inside a resolved object or array, an integer beyond
Number.MAX_SAFE_INTEGER is
carried as its exact decimal string, at any depth - OpenFeature’s JsonValue has
no numeric slot that holds it exactly, and the decimal string is the lossless one, so
the resolved value survives JSON.stringify. Ordinary numbers are untouched; parse
the string with BigInt(value) where the magnitude matters. The scalar
getNumberValue answers TYPE_MISMATCH for the same value on purpose.
Non-boolean resolutions are never targeted. String, number and object evaluations map to configuration values, and targeted evaluation is a flag concept - there is no such thing as a targeted config value.
Framework examples
Svelte, Solid, Lit, or plain DOM code
React (with @openfeature/react-sdk)
Server-side rendering
The provider opens no event stream where the environment cannot hold one, so it is safe to construct during a server-side rendering pass. A connection opened there is never torn down by anything the framework calls, and it holds the request open.When the stream never connects at all
A stream that fails from the start is a different problem from one that drops: the provider reachesREADY, every evaluation is answered from the cache, and the values are correct once and then never change. It looks like a working integration.
The client says so once, after it gives up on its first connection:
console.warn if you configured none, so the message reaches the consumer least likely to be looking for it. A connection lost after one succeeded is not reported this way - the stream observer covers that.
The usual causes in a browser are CORS-shaped. The stream request carries X-Nexus-API-Key, which is not a CORS-safelisted header, so the browser preflights it: the endpoint has to allow this origin and name that header in Access-Control-Allow-Headers. A blocked preflight is indistinguishable from an unreachable server to the SDK, which is why the message names both.
Stopping the provider
onClose(), which releases the event stream, every timer, every listener and the identity the provider was holding.
Notes
- Public key only. Use a
wxp_...browser key. Secret backend keys (wxs_...) are not accepted. - No secrets. Secret values are never served to a browser key. Only feature flags and configuration are available.
