Overview01
github.com/xraph/forge/extensions/features provides feature flag management with pluggable
providers. It registers a Service in the DI container that evaluates feature flags with user
targeting, percentage rollouts, caching, and periodic refresh from the upstream provider.
What It Registers02
| Service | DI Key | Type |
| Feature flags service | features | *Service |
The service is registered with Vessel and supports the legacy alias features.Service.
Quick Start03
package main
import (
"context"
"fmt"
"time"
"github.com/xraph/forge"
"github.com/xraph/forge/extensions/features"
)
func main() {
app := forge.NewApp(forge.AppConfig{Name: "my-app", Version: "1.0.0"})
// Register with local (config-based) provider
app.RegisterExtension(features.NewExtension(
features.WithProvider("local"),
features.WithRefreshInterval(30 * time.Second),
features.WithCache(true, 5*time.Minute),
features.WithDefaultFlags(map[string]any{
"new-dashboard": true,
"max-items": 100,
}),
features.WithLocalFlags(map[string]features.FlagConfig{
"new-dashboard": {
Key: "new-dashboard",
Enabled: true,
Type: "boolean",
Value: true,
},
"dark-mode": {
Key: "dark-mode",
Enabled: true,
Type: "boolean",
Value: false,
Targeting: []features.TargetingRule{
{
Attribute: "group",
Operator: "in",
Values: []string{"beta-testers", "internal"},
Value: true,
},
},
},
"rollout-feature": {
Key: "rollout-feature",
Enabled: true,
Type: "boolean",
Value: false,
Rollout: &features.RolloutConfig{
Percentage: 25, // 25% of users
Attribute: "user_id",
},
},
}),
))
ctx := context.Background()
app.Start(ctx)
defer app.Stop(ctx)
// Resolve the feature flags service
svc := features.MustGet(app.Container())
// Build a user context for targeted evaluation
userCtx := features.NewUserContext("user-123").
WithEmail("[email protected]").
WithGroups([]string{"beta-testers"}).
WithAttribute("plan", "premium")
// Boolean flag check
if svc.IsEnabled(ctx, "new-dashboard", userCtx) {
fmt.Println("New dashboard is enabled!")
}
// Targeted flag -- enabled for beta-testers
if svc.IsEnabled(ctx, "dark-mode", userCtx) {
fmt.Println("Dark mode enabled for beta tester")
}
// Typed value retrieval
maxItems := svc.GetInt(ctx, "max-items", userCtx, 50)
fmt.Println("Max items:", maxItems)
// Get all flags at once
allFlags, _ := svc.GetAllFlags(ctx, userCtx)
fmt.Println("All flags:", allFlags)
}Using Feature Flags in Your Services04
Inject the feature flags service via constructor injection to gate functionality:
package handlers
import (
"net/http"
"github.com/xraph/forge"
"github.com/xraph/forge/extensions/features"
)
type DashboardHandler struct {
features *features.Service
}
func NewDashboardHandler(svc *features.Service) *DashboardHandler {
return &DashboardHandler{features: svc}
}
func (h *DashboardHandler) GetDashboard(ctx forge.Context) error {
// Build user context from the authenticated user
userCtx := features.NewUserContext(ctx.Get("user_id").(string)).
WithEmail(ctx.Get("email").(string))
data := map[string]any{
"version": "v1",
}
// Gate features based on flags
if h.features.IsEnabled(ctx.Context(), "new-dashboard", userCtx) {
data["version"] = "v2"
data["widgets"] = h.getNewWidgets()
}
// Use typed flags for configuration
data["maxItems"] = h.features.GetInt(ctx.Context(), "max-items", userCtx, 50)
data["theme"] = h.features.GetString(ctx.Context(), "theme", userCtx, "light")
return ctx.JSON(http.StatusOK, data)
}
func (h *DashboardHandler) getNewWidgets() []string {
return []string{"chart", "timeline", "activity"}
}Forcing a Refresh05
You can programmatically force a refresh of flag values from the provider:
svc := features.MustGet(app.Container())
// Force refresh from the upstream provider
if err := svc.Refresh(ctx); err != nil {
log.Println("flag refresh failed:", err)
}Key Concepts06
Providers -- choose between
local(config-based),launchdarkly,unleash,flagsmith, orposthog. All implement the sameProviderinterface. The local provider is fully functional; external providers require their respective SDKs.Flag evaluation -- check if a flag is enabled, get string/int/float values, or retrieve complex JSON values. All evaluations support user targeting context.
User context -- build a
UserContextwith user ID, email, name, groups, attributes, IP, and country for targeted flag evaluation.Targeting rules -- define rules that match on user attributes using operators like
equals,contains,in, andnot_in.Percentage rollouts -- gradually roll out features to a percentage of users using consistent hashing on a user attribute.
Caching -- optional in-memory cache with configurable TTL (default 5 minutes) to reduce provider calls.
Periodic refresh -- automatically refresh flag values from the provider at a configurable interval (default 30 seconds).
Important Runtime Notes07
The
localprovider is fully functional and does not require external infrastructure.External providers (LaunchDarkly, Unleash, Flagsmith, PostHog) have provider implementations in the
providers/package but are not yet wired into the extension factory -- callingNewExtensionwith those provider names will return an error.Default flag values can be set in config and are used when the provider returns no value.
On errors, the service returns default values and logs warnings rather than failing.
The service runs a background goroutine for periodic refresh, which is cleanly shut down on
Stop().