Skip to main content

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.