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-sdkQuick 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
)| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | required | Environment API key from dashboard |
WithBaseURL | string | https://api.flipswitch.io | Your Flipswitch server URL |
WithRealtime | bool | true | Enable SSE for real-time flag updates |
WithHTTPClient | *http.Client | default | Custom HTTP client |
WithPollingFallback | bool | true | Fall back to polling when SSE fails |
WithPollingInterval | time.Duration | 30s | Polling interval for fallback mode |
WithMaxSseRetries | int | 5 | Max 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:
- The SSE client receives a
flag-updatedorconfig-updatedevent - The local cache is immediately invalidated
- 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 startUse 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.