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).

GitHub: github.com/flipswitch-io/go-sdk

Requirements

  • Go 1.24+
  • OpenFeature SDK

Installation

go get github.com/flipswitch-io/go-sdk
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-io/go-sdk"
)

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.io"),  // Optional: defaults to production
    flipswitch.WithRealtime(true),                         // Optional: defaults to true
    flipswitch.WithPollingFallback(true),                  // Optional: fall back to polling when SSE fails
    flipswitch.WithPollingInterval(30 * time.Second),      // Optional: polling interval
    flipswitch.WithMaxSseRetries(5),                       // Optional: max SSE retries before fallback
    flipswitch.WithHTTPClient(customClient),               // Optional: custom http.Client
)
OptionTypeDefaultDescription
apiKeystringrequiredEnvironment API key from dashboard
WithBaseURLstringhttps://api.flipswitch.ioYour Flipswitch server URL
WithRealtimebooltrueEnable SSE for real-time flag updates
WithHTTPClient*http.ClientdefaultCustom HTTP client
WithPollingFallbackbooltrueFall back to polling when SSE fails
WithPollingIntervaltime.Duration30sPolling interval for fallback mode
WithMaxSseRetriesint5Max SSE retries before polling fallback

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-updated or config-updated event
  2. The local cache is immediately invalidated
  3. Next flag evaluation fetches the fresh value

Event Listeners

provider.AddFlagChangeListener(func(event flipswitch.FlagChangeEvent) {
    if event.FlagKey != "" {
        fmt.Printf("Flag changed: %s\n", event.FlagKey)
    } else {
        fmt.Println("All flags invalidated")
    }
})

Connection Status

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

// Force reconnect
provider.ReconnectSse()

Polling Fallback

When SSE connection fails repeatedly, the SDK falls back to polling:

provider, err := flipswitch.NewProvider(
    "YOUR_API_KEY",
    flipswitch.WithPollingFallback(true),  // default: true
    flipswitch.WithPollingInterval(30 * time.Second),
    flipswitch.WithMaxSseRetries(5),
)

// Check if polling is active
if provider.IsPollingActive() {
    fmt.Println("Polling fallback is active")
}

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-io/go-sdk"
)

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-io/go-sdk"
)

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 resumes normal operation.

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.

On this page