Documentation
¶
Overview ¶
Package featureflag provides middleware that attaches a fixed set of boolean feature flags to every request, letting downstream handlers gate behavior on named features. It is a small, dependency-free analogue of the request-scoped flag lookup exposed by Node feature-flag libraries such as "express-feature-flags" or the flag helpers bundled with LaunchDarkly's and Unleash's Express SDKs: rather than talking to a remote flag service, it carries a caller-supplied map of names to enabled states and makes it available for the lifetime of the request.
Use this middleware when you want to toggle code paths (a new UI, an experimental endpoint, a gradual rollout) without redeploying or littering handlers with global configuration reads. Because the flag set is captured once when New is called, it is best for process-wide flags resolved at startup; if you need per-user or dynamically refreshed flags, build the map per request in your own middleware and store it under the same request state, or wrap New so it recomputes the map on each call.
Mechanically the middleware sits anywhere in the chain before the handlers that read flags. On each request it calls req.Set with the internal context key to stash the flag map on the request, then immediately calls next() to continue the chain. It never writes a response, never sets a header, and never short-circuits, so it is safe to mount globally with app.Use. Handlers (or nested middleware) retrieve a flag by calling the package function Enabled, which reads the map back out of the request state.
The stored map is shared by reference across all requests: New copies the Options.Flags pointer, so mutating the original map after New returns will be observed by in-flight requests and is not safe for concurrent writes. Treat the map as read-only once passed in. Enabled is defensively written to degrade gracefully — if the middleware never ran, if the stored value has an unexpected type, or if the flag name is unknown, it returns false rather than panicking, so an unregistered flag and a flag explicitly set to false are indistinguishable to callers.
Compared with the Node originals this port is intentionally minimal: there is no percentage rollout, no user targeting, no remote evaluation, and no streaming updates — just a static name-to-bool table resolved locally. That keeps it stdlib-only and allocation-light, at the cost of the dynamic evaluation features a full flag platform provides. If Options.Flags is nil an empty map is substituted, so Enabled always reports false and the middleware is a harmless no-op.
Example ¶
Example wires the feature-flag middleware into an express application and drives it with net/http/httptest. It registers two flags at startup — one enabled and one disabled — via featureflag.Options, then mounts the middleware globally with app.Use so every request carries the flag set. The route handler queries the flags through featureflag.Enabled, which recovers the map the middleware stashed on the request. A flag that was never registered ("nope") reports false, demonstrating the graceful-degradation semantics. The printed lines are deterministic, so the Output block asserts the exact flag states seen by the handler.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/malcolmston/express"
"github.com/malcolmston/express/middleware/featureflag"
)
func main() {
app := express.New()
app.Use(featureflag.New(featureflag.Options{
Flags: map[string]bool{"new-ui": true, "beta": false},
}))
app.Get("/", func(req *express.Request, res *express.Response, next express.Next) {
fmt.Println("new-ui:", featureflag.Enabled(req, "new-ui"))
fmt.Println("beta:", featureflag.Enabled(req, "beta"))
fmt.Println("nope:", featureflag.Enabled(req, "nope"))
res.Send("ok")
})
rec := httptest.NewRecorder()
app.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
}
Output: new-ui: true beta: false nope: false
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.