Go SDK

Flipswitch SDK for Go with real-time SSE support

The Flipswitch Go SDK provides an OpenFeature-compatible provider with automatic cache invalidation via Server-Sent Events (SSE).

Requirements

  • Go 1.21+
  • OpenFeature SDK

Installation

go get github.com/flipswitch-dev/flipswitch/sdks/go
go get github.com/open-feature/go-sdk

Quick Start

package main
 
import (
    "context"
    "fmt"
 
    "github.com/open-feature/go-sdk/openfeature"
    flipswitch "github.com/flipswitch-dev/flipswitch/sdks/go"
)
 
func main() {
    // Only API key is required
    provider, err := flipswitch.NewProvider("YOUR_API_KEY")
    if err != nil {
        panic(err)
    }
    defer provider.Shutdown()
 
    // Initialize the provider
    if err := provider.Init(openfeature.EvaluationContext{}); err != nil {
        panic(err)
    }
 
    // Register with OpenFeature
    openfeature.SetProvider(provider)
    client := openfeature.NewClient("my-app")
 
    // Evaluate flags
    ctx := context.Background()
    darkMode, _ := client.BooleanValue(ctx, "dark-mode", false, openfeature.EvaluationContext{})
    welcomeMessage, _ := client.StringValue(ctx, "welcome-message", "Hello!", openfeature.EvaluationContext{})
    maxItems, _ := client.IntValue(ctx, "max-items-per-page", 10, openfeature.EvaluationContext{})
 
    fmt.Printf("Dark mode: %v\n", darkMode)
    fmt.Printf("Welcome: %s\n", welcomeMessage)
    fmt.Printf("Max items: %d\n", maxItems)
}

Configuration Options

provider, err := flipswitch.NewProvider(
    "YOUR_API_KEY",
    flipswitch.WithBaseURL("https://api.flipswitch.dev"),  // Optional: defaults to production
    flipswitch.WithRealtime(true),                         // Optional: defaults to true
    flipswitch.WithHTTPClient(customClient),               // Optional: custom http.Client
)
OptionTypeDefaultDescription
apiKeystringrequiredEnvironment API key from dashboard
WithBaseURLstringhttps://api.flipswitch.devYour Flipswitch server URL
WithRealtimebooltrueEnable SSE for real-time flag updates
WithHTTPClient*http.ClientdefaultCustom HTTP client

Evaluation Context

Pass user attributes for targeting:

evalCtx := openfeature.NewEvaluationContext(
    "user-123",  // targeting key
    map[string]interface{}{
        "email":   "user@example.com",
        "plan":    "premium",
        "country": "SE",
    },
)
 
showFeature, _ := client.BooleanValue(ctx, "new-feature", false, evalCtx)

Real-Time Updates

When WithRealtime(true) is set (default), the SDK maintains an SSE connection to receive instant flag changes:

  1. The SSE client receives a flag-change event
  2. The local cache is immediately invalidated
  3. Next flag evaluation fetches the fresh value

Event Listeners

provider.AddFlagChangeListener(func(event flipswitch.FlagChangeEvent) {
    fmt.Printf("Flag changed: %s\n", event.FlagKey)
    fmt.Printf("Timestamp: %s\n", event.Timestamp)
})

Connection Status

// Check current SSE status
status := provider.GetSseStatus()
// flipswitch.StatusConnecting, StatusConnected, StatusDisconnected, StatusError
 
// Force reconnect
provider.ReconnectSse()

Detailed Evaluation

Get full evaluation details including variant and reason:

details, _ := client.BooleanValueDetails(ctx, "feature-flag", false, evalCtx)
 
fmt.Printf("Value: %v\n", details.Value)
fmt.Printf("Variant: %s\n", details.Variant)
fmt.Printf("Reason: %s\n", details.Reason)

Bulk Flag Evaluation

Evaluate all flags at once:

// Evaluate all flags
flags := provider.EvaluateAllFlags(openfeature.FlattenedContext{
    "targetingKey": "user-123",
    "email": "user@example.com",
})
 
for _, flag := range flags {
    fmt.Printf("%s (%s): %s\n", flag.Key, flag.ValueType, flag.GetValueAsString())
}
 
// Evaluate a single flag with full details
flag := provider.EvaluateFlag("dark-mode", context)
if flag != nil {
    fmt.Printf("Value: %v\n", flag.Value)
    fmt.Printf("Reason: %s\n", flag.Reason)
    fmt.Printf("Variant: %s\n", flag.Variant)
}

HTTP Server Integration

package main
 
import (
    "context"
    "encoding/json"
    "net/http"
 
    "github.com/open-feature/go-sdk/openfeature"
    flipswitch "github.com/flipswitch-dev/flipswitch/sdks/go"
)
 
var client *openfeature.Client
 
func main() {
    provider, _ := flipswitch.NewProvider("YOUR_API_KEY")
    defer provider.Shutdown()
    provider.Init(openfeature.EvaluationContext{})
 
    openfeature.SetProvider(provider)
    client = openfeature.NewClient("my-app")
 
    http.HandleFunc("/feature", featureHandler)
    http.ListenAndServe(":8080", nil)
}
 
func featureHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.URL.Query().Get("user_id")
 
    evalCtx := openfeature.NewEvaluationContext(userID, nil)
    enabled, _ := client.BooleanValue(context.Background(), "new-feature", false, evalCtx)
 
    json.NewEncoder(w).Encode(map[string]bool{"feature_enabled": enabled})
}

Gin Integration

package main
 
import (
    "context"
 
    "github.com/gin-gonic/gin"
    "github.com/open-feature/go-sdk/openfeature"
    flipswitch "github.com/flipswitch-dev/flipswitch/sdks/go"
)
 
func main() {
    provider, _ := flipswitch.NewProvider("YOUR_API_KEY")
    defer provider.Shutdown()
    provider.Init(openfeature.EvaluationContext{})
 
    openfeature.SetProvider(provider)
    client := openfeature.NewClient("my-app")
 
    r := gin.Default()
 
    r.GET("/feature", func(c *gin.Context) {
        userID := c.Query("user_id")
 
        evalCtx := openfeature.NewEvaluationContext(userID, nil)
        enabled, _ := client.BooleanValue(context.Background(), "new-feature", false, evalCtx)
 
        c.JSON(200, gin.H{"feature_enabled": enabled})
    })
 
    r.Run(":8080")
}

Reconnection Strategy

The SSE client automatically reconnects with exponential backoff:

  • Initial delay: 1 second
  • Maximum delay: 30 seconds
  • Backoff multiplier: 2x

When reconnected, the provider continues to work seamlessly.

Shutdown

Always shutdown the provider when done:

provider.Shutdown()
// or defer provider.Shutdown() at the start

Use defer provider.Shutdown() right after creating the provider to ensure proper cleanup when your application exits.

Troubleshooting

SSE connection keeps disconnecting

Check if your proxy or load balancer supports long-lived connections. Configure timeout settings accordingly.

Flags not updating in real-time

Verify that WithRealtime(true) is set and check the SSE status:

fmt.Println(provider.GetSseStatus())

Context cancellation

Make sure your context isn't being cancelled prematurely. Use context.Background() for long-running operations.