OpenFeature with ConfigDirector
OpenFeature is an open standard for feature flag evaluation, a CNCF project with an SDK for each major language. Your application codes against the OpenFeature API, and a provider connects that API to a feature flag service. ConfigDirector ships a provider for every platform it has an SDK for, so an application on OpenFeature uses ConfigDirector by registering the ConfigDirector provider, and could move to another service by registering another one.
Each provider wraps the ConfigDirector SDK for its platform. Everything the SDK does, the provider does too: it streams updates, evaluates locally on servers, reports the evaluations that drive Evaluation Activity, the config lifecycle, and alerts, and is metered the same way. The only difference is the API your code calls.
The providers
| Platform | Provider | Pairs with | SDK key |
|---|---|---|---|
| Web | @configdirector/openfeature-web-provider on npm | OpenFeature Web SDK, @openfeature/web-sdk | Client |
| Swift | configdirector-swift-openfeature-provider, through Swift Package Manager | OpenFeature Swift SDK, 0.6.0 or newer | Client |
| Android | com.configdirector:openfeature-android-provider, from the ConfigDirector Maven repository | OpenFeature Kotlin SDK, 0.8.0 or newer | Client |
| Flutter | configdirector_openfeature_flutter_provider on pub.dev, in beta alongside the OpenFeature Dart client SDK | OpenFeature Dart client SDK | Client |
| Node.js | @configdirector/openfeature-server-provider on npm | OpenFeature Node.js SDK, @openfeature/server-sdk | Server |
| Python | configdirector-openfeature-server-provider on PyPI | OpenFeature Python SDK | Server |
| Java | com.configdirector:openfeature-server-provider, from the ConfigDirector Maven repository | OpenFeature Java SDK | Server |
| .NET | ConfigDirector.OpenFeature.ServerProvider on NuGet | OpenFeature .NET SDK, 2.14.1 or newer | Server |
| Go | github.com/ConfigDirector/go-sdks/configdirector-openfeature | OpenFeature Go SDK | Server |
The four client providers follow OpenFeature's static-context model: the application sets one evaluation context, the provider sends it to ConfigDirector, and receives every config already evaluated for it. Changing the context reconnects with the new one. They take a client SDK key, which is not secret.
The five server providers follow the dynamic-context model: the provider receives the targeting rules and segments and evaluates them in memory, so every evaluation can carry its own context and makes no network call. They take a server SDK key, which is a secret: provide it through an environment variable, never in the code.
Each provider accepts the same configuration options as the SDK it wraps, such as metadata with the app name and version, the connection mode and timeout, and a logger. Each provider page links to the options of its SDK.
Register the provider
Registering a provider is the same three steps on every platform: create the provider with the SDK key, set it on the OpenFeature API and wait, and get a client. In Node.js:
import { OpenFeature } from "@openfeature/server-sdk";
import { ConfigDirectorProvider } from "@configdirector/openfeature-server-provider";
const provider = new ConfigDirectorProvider("YOUR-SERVER-SDK-KEY");
await OpenFeature.setProviderAndWait(provider);
const client = OpenFeature.getClient();
And in the browser:
import { OpenFeature } from "@openfeature/web-sdk";
import { ConfigDirectorProvider } from "@configdirector/openfeature-web-provider";
const provider = new ConfigDirectorProvider("YOUR-CLIENT-SDK-KEY");
await OpenFeature.setProviderAndWait(provider);
const client = OpenFeature.getClient();
The wait returns once the first config values arrive, or when the connection timeout elapses, 3 seconds by default. After a timeout the provider reports an error status and keeps connecting in the background, then reports ready as soon as values arrive. Until then, every evaluation returns the in-code default value you pass, with the PROVIDER_NOT_READY error code in its details.
Reading configs
The OpenFeature getters read ConfigDirector configs by type:
| OpenFeature getter | ConfigDirector config type |
|---|---|
| Boolean | Boolean, including feature flags and kill switches |
| String | String, Enum, and URL |
| Number, or integer and double where the SDK separates them | Integer, Float, and Timespan. A timespan is the number exactly as entered in the dashboard, in its own unit. |
| Object | JSON, as the SDK's structure or list |
The in-code default value you pass is returned when the config cannot be evaluated. The detailed getters, such as getBooleanDetails, say why:
| Outcome | Reason | Error code |
|---|---|---|
| A value was found | TARGETING_MATCH | |
| The config carries no value | DEFAULT | |
| The config key is unknown | ERROR | FLAG_NOT_FOUND |
| No config values have arrived yet | ERROR | PROVIDER_NOT_READY |
| The value does not match the requested type | ERROR | TYPE_MISMATCH |
When a value was found, the variant in the details is ConfigDirector's identifier for that value, a stable hash you can send to analytics in place of the value itself. The last two errors also show up in the dashboard as alerts, since a config the application cannot find, or reads with the wrong type, is usually a mistake worth fixing.
The evaluation context
An OpenFeature evaluation context maps onto a ConfigDirector context like this:
| OpenFeature evaluation context | ConfigDirector context |
|---|---|
The targetingKey, or otherwise an id attribute | The identifier, id |
A top-level kind attribute | The context kind, such as user, device, or tenant. Leaving it out means user. |
A name attribute | name |
A traits attribute holding a structure | Traits, the attributes targeting rules can test |
A boolean anonymous attribute | An anonymous context, evaluated but never stored or shown in the dashboard |
Any other attribute is ignored. This is the one thing to know when moving from another provider: put the values your targeting rules depend on inside traits, not at the top level of the context.
await OpenFeature.setContext({
targetingKey: "user-123",
name: "Example User",
traits: {
plan: "enterprise",
country: "US",
},
});
A context kind other than user is set with kind:
await OpenFeature.setContext({
targetingKey: "acme",
kind: "tenant",
name: "Acme Inc.",
traits: { plan: "enterprise" },
});
On the client providers the context is set on the OpenFeature API, for the whole application. On the server providers it is the argument of each getter, or the client's or API's context when every evaluation shares one. Either way, the targetingKey is required for percentage rollouts to be stable: contexts are assigned by their context kind and identifier.
Events
The providers emit the OpenFeature provider events, so code that follows flags through events works with ConfigDirector as it does with any provider:
- Ready once the first config values arrive, including after a timeout.
- Configuration changed whenever configs are updated in the dashboard, through the Admin API, Terraform, or the MCP server, carrying the keys of the configs that changed. In a client application, this is the event to re-read flags on.
- Error when the initial connection times out.
- Reconciling and context changed around a context update, on the client providers.
Each provider page shows how its OpenFeature SDK exposes these: a Combine publisher in Swift, a flow in Kotlin, handlers in Flutter, Node.js, .NET, and Go, and so on.
Test your code
Code that reads flags through OpenFeature is tested by swapping the provider, so it never has to change for a test and nothing from ConfigDirector is involved. The OpenFeature SDKs for the web, Node.js, Python, Java, .NET, Go, and Dart ship an in-memory provider that serves the values your test sets. The OpenFeature Swift and Kotlin SDKs ship none yet, so the ConfigDirector providers for Swift and Android accept the test client from the ConfigDirector SDK's testing entry point instead. Each provider page has a Test your code section with an example.
OpenFeature hooks and tracking
Hooks you add to the OpenFeature API or to a client run before and after each evaluation with the ConfigDirector provider as with any other, so an OpenTelemetry or error-monitoring hook from the OpenFeature ecosystem works unchanged. The providers do not implement the OpenFeature tracking API, and ConfigDirector does not offer an OFREP endpoint: use the provider for your platform.
The provider or the native SDK?
Choose the provider when your application already reads flags through OpenFeature, when you want the option of changing services without touching application code, or when you use OpenFeature's ecosystem of hooks. The ConfigDirector dashboard, targeting rules, monitoring, and alerts work the same either way.
Choose the native SDK for conveniences the OpenFeature API does not have: watch and the value streams and flows that deliver each change as a value, the React, Vue, Next.js, and Nuxt SDKs with their hooks and composables, the Jetpack Compose and SwiftUI helpers, and the evaluation reason of every read in the same spelling across every SDK.
Switching between the two later is a code change in one place, since the provider and the SDK share the SDK key, the configs, and the targeting rules.