Quarkus Feature Flags - OpenFeature
OpenFeature is a vendor-neutral, community-driven standard for feature flagging.
This extension integrates OpenFeature as a backend for Quarkus Feature Flags.
Applications use the familiar quarkus-flags API (@Feature, Flags) while flag values are resolved via the OpenFeature Java SDK, which can be backed by any OpenFeature-compatible provider (LaunchDarkly, Flagsmith, CloudBees, Split, etc.).
Dependency
Add the io.quarkiverse.flags:quarkus-flags-openfeature extension to your build file.
For instance, with Maven, add the following dependency to your POM file:
<dependency>
<groupId>io.quarkiverse.flags</groupId>
<artifactId>quarkus-flags-openfeature</artifactId>
<version>{project-version}</version>
</dependency>
You also need to add a concrete OpenFeature provider dependency to your project (e.g. LaunchDarkly, Flagsmith, etc.). The OpenFeature Java SDK is already included as a transitive dependency.
Setting up the OpenFeature Provider
The concrete OpenFeature provider must be registered with the OpenFeatureAPI before flags can be evaluated.
This is typically done at application startup:
import dev.openfeature.sdk.OpenFeatureAPI;
import jakarta.enterprise.event.Startup;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class OpenFeatureSetup {
@Startup
void onStart() {
// Register a concrete provider, e.g. LaunchDarkly, Flagsmith, etc.
OpenFeatureAPI.getInstance().setProviderAndWait(myProvider);
}
}
Registering Flags
OpenFeature does not provide a way to discover available flags.
Therefore, flags must be registered with the OpenFeatureFlags registry before they can be evaluated.
There are two ways to register flags.
Configuration-based Registration
Flags can be declared in application.properties.
These flags are registered automatically at startup:
quarkus.flags.openfeature.dark-mode.type=boolean (1)
quarkus.flags.openfeature.dark-mode.default-value=false (2)
quarkus.flags.openfeature.greeting.type=string
quarkus.flags.openfeature.greeting.default-value=Hello
quarkus.flags.openfeature.max-retries.type=int
quarkus.flags.openfeature.max-retries.default-value=3
quarkus.flags.openfeature.ratio.type=double
quarkus.flags.openfeature.ratio.default-value=0.0
| 1 | The type determines which OpenFeature evaluation method is used. Supported values: boolean, string, int, double. |
| 2 | The default value is returned by OpenFeature if the flag cannot be resolved by the backend provider. |
Both type and default-value are required for each flag. The application will fail to start if either is missing.
|
Programmatic Registration
Additional flags can be registered at runtime via the OpenFeatureFlags CDI bean:
import io.quarkiverse.flags.openfeature.OpenFeatureFlags;
import jakarta.inject.Inject;
@Inject
OpenFeatureFlags openFeatureFlags;
// Register a boolean flag
openFeatureFlags.register("new-feature", false);
// Register a string flag
openFeatureFlags.register("welcome-message", "Hello");
// Register an integer flag
openFeatureFlags.register("max-retries", 3);
// Register a double flag
openFeatureFlags.register("ratio", 0.5);
// Unregister a flag
openFeatureFlags.unregister("old-feature");
// Check if a flag is registered
openFeatureFlags.isRegistered("new-feature"); // true
Using Flags
Once registered, flags are resolved through the standard quarkus-flags API:
import io.quarkiverse.flags.Flag;
import io.quarkiverse.flags.Flags;
import io.quarkiverse.flags.Feature;
import jakarta.inject.Inject;
@Inject
@Feature("dark-mode")
Flag darkMode;
@Inject
Flags flags;
// Boolean flag
if (darkMode.isEnabled()) {
// ...
}
// String flag
String greeting = flags.getString("greeting");
// Integer flag
int retries = flags.getInt("max-retries");
// Double flag (returned as BigDecimal)
BigDecimal ratio = flags.getDecimal("ratio");
Provider Ordering
The OpenFeature flag provider is ordered after the in-memory provider and before the config provider.
This means:
-
In-memory flags (highest priority) - useful for testing overrides
-
OpenFeature flags
-
Config-based flags (lowest priority)
Evaluation Context
The quarkus-flags ComputationContext is forwarded to the OpenFeature EvaluationContext.
The targetingKey entry is mapped to the OpenFeature targeting key; all other entries are mapped to context attributes:
Flag.ComputationContext ctx = Flag.ComputationContext.builder()
.put("targetingKey", "user-123")
.put("email", "user@example.com")
.build();
flag.compute(ctx);