Integrations
This page shows Go-idiomatic ways to integrate feature flags into common patterns.
HTTP middleware (net/http)
Use the togglyhttp package to:
- attach an evaluation context to each request
- gate handlers behind a feature
MiddlewareWith builds a request-scoped context before the router runs. Supply identity, groups, and claims from your authentication layer; the SDK fills missing request fields from HTTP headers. No global user identity is changed.
import (
"net/http"
"github.com/ops-ai/Toggly.FeatureManagement/toggly-go/togglyhttp"
)
// Call this after authentication middleware has populated the request context.
// The callbacks are your application's functions for reading its verified principal.
func featureHandler(client togglyhttp.Evaluator, next http.Handler,
identity func(*http.Request) string,
groups func(*http.Request) []string,
claims func(*http.Request) map[string]string,
) http.Handler {
mux := http.NewServeMux()
mux.Handle("/admin", togglyhttp.FeatureGate(client, "AdminUI")(next))
return togglyhttp.MiddlewareWith(togglyhttp.Options{
GetIdentity: identity,
GetGroups: groups,
GetClaims: claims,
})(mux)
}
User-Agent and Accept-Language populate browser, language, device, and OS inputs. Country uses the first non-empty CF-IPCountry, X-Vercel-IP-Country, or CloudFront-Viewer-Country header. These are request metadata, not authentication. Only trust country headers that your own proxy overwrites or validates.
For a complete custom context, use Middleware(build) or Options.GetContext. Non-empty fields in its Request override header-derived values; missing fields are filled from headers. When GetContext is set, the other GetIdentity/GetGroups/GetClaims callbacks are ignored. Evaluation context explains this merge.
Customizing behavior
FeatureGate supports options:
togglyhttp.WithDenyHandler(...)(default: 404)togglyhttp.WithErrorHandler(...)(default: 503)togglyhttp.WithContextBuilder(...)(if you don’t use the middleware)
Templates (html/template)
Use the togglytemplate package to add template functions:
feature:{{ if (feature . "MyFeature") }} ... {{ end }}featureAny:{{ if (featureAny . "A" "B") }} ... {{ end }}featureAll:{{ if (featureAll . "A" "B") }} ... {{ end }}
import (
"html/template"
"github.com/ops-ai/Toggly.FeatureManagement/toggly-go/toggly"
"github.com/ops-ai/Toggly.FeatureManagement/toggly-go/togglytemplate"
)
type PageData struct {
UserID string
}
func (p PageData) TogglyContext() toggly.Context {
return toggly.Context{Identity: p.UserID}
}
func buildTemplates(client *toggly.Client) *template.Template {
return template.Must(
template.New("base").
Funcs(togglytemplate.FuncMap(client, nil)).
ParseFiles("base.html"),
)
}
Context helpers (context.Context)
If you want to carry a toggly.Context through your call stack, use togglyctx:
import (
"context"
"github.com/ops-ai/Toggly.FeatureManagement/toggly-go/toggly"
"github.com/ops-ai/Toggly.FeatureManagement/toggly-go/togglyctx"
)
ctx := togglyctx.With(context.Background(), toggly.Context{
Identity: "user-123",
Groups: []string{"beta"},
})
// IsEnabled fills empty per-call fields from the ambient context.
on, err := client.IsEnabled(ctx, "MyFeature", toggly.Context{})
Use r.Context() inside handlers to preserve request cancellation. togglyctx.From(ctx) returns two values, (toggly.Context, bool), if you need to inspect whether context exists. togglyctx.Resolve(ctx, overrides) exposes the same ambient/per-call merge without evaluating a flag.
The template helpers evaluate with context.Background() and obtain evaluation context from template data. Pass data implementing TogglyContext() (as above), or pass a toggly.Context directly. They do not inherit the HTTP request automatically. When cancellation or explicit error handling matters, evaluate in the handler first and render the result.