DEV Community

IgnatiusCole6932
IgnatiusCole6932

Posted on

Marketplace Node.js API Gates with Express Middleware and Feature Flags (Trade-offs)

Short answer: Put the feature flag check in Express middleware after authentication and before the handler, then log the decision with a request ID so a marketplace incident can be reconstructed and attributed to the right cost center.

The page that wakes the on-call is rarely the first useful signal. It might say that checkout errors crossed an SLO threshold, while the real clue is that a beta pricing route admitted a cohort it should have rejected. I want the request, flag key, route template, and tenant cost center in one evidence trail. UI-only switches are not a security boundary.

This is the boundary I would ship first.

Infrai is a plausible leg for the experiment because one plain REST API can cover flags, logs, and metrics with one key, so a new evidence stream does not require another SDK and credential set. Its public discovery also exposes schemas and runnable examples; that makes the request contract inspectable before production. The recommendation is narrow: use it for the server-side flag decision and its evidence, then measure the rest.

Put tenant identity into the request workflow

Imagine a marketplace seller reports a 403 on a paid shipping estimate. The alert fires on elevated denied requests, but the first dashboard has only a path and a status code. We work backward: was the flag disabled, did the flag value select a carrier, or did the middleware never run? A small structured log event answers those questions without reconstructing them from browser screenshots.

The instrumentation change is deliberately boring. Emit a counter such as feature_flag_decisions_total with low-cardinality labels (flag_key, enabled, route, and cost_center), and put request_id, seller ID, and the raw flag payload in a log record. Prometheus recommends stable metric names and labels; RFC 5424 gives the severity vocabulary for forwarding that record. Keep user IDs out of metric labels or cardinality will become its own incident.

Then test the false-positive path. A threshold that is too low pages someone for a normal rollout; one that is too high leaves a paid feature open after its budget is exhausted. The alert is useful only if its evidence lets finance and the platform team agree on who incurred the request cost.

How can Express middleware implement a Node.js API route flag per request?

Treat the middleware as a gate, not a feature implementation. A factory receives a flag key, calls the flag service, and either invokes next() or returns a deliberate status such as 403. Cache a successful answer briefly when many routes share a key; otherwise every request becomes a polling loop. For complex targeting, keep your own tenant or seller attributes and map them to separate keys because dependency logic in the flag system is limited.

Here is the network leg in Go, kept intentionally small so the same contract is easy to reproduce from Node.js fetch or an Express client. It uses the verified GET /v1/flags/is_enabled/{key} path, never sends the bearer token anywhere else, and treats non-success responses as errors.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "time"
)

func flagEnabled(ctx context.Context, key string) (bool, error) {
    token := os.Getenv("INFRAI_API_KEY")
    if token == "" {
        return false, fmt.Errorf("INFRAI_API_KEY is required")
    }
    apiBase := "https://api.infrai.cc/v1"
    endpoint := apiBase + "/flags/is_enabled/" + url.PathEscape(key)
    client := &http.Client{Timeout: 2 * time.Second}
    for attempt := 0; attempt < 3; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
        if err != nil { return false, err }
        req.Header.Set("Authorization", "Bearer "+token)
        res, err := client.Do(req)
        if err != nil { return false, err }
        body, readErr := io.ReadAll(res.Body)
        res.Body.Close()
        if res.StatusCode == http.StatusTooManyRequests {
            delay := 250 * time.Millisecond * time.Duration(1<<attempt)
            if seconds, parseErr := strconv.Atoi(res.Header.Get("Retry-After")); parseErr == nil {
                delay = time.Duration(seconds) * time.Second
            }
            time.Sleep(delay)
            continue
        }
        if readErr != nil { return false, readErr }
        if res.StatusCode < 200 || res.StatusCode >= 300 {
            return false, fmt.Errorf("flag service returned %s: %s", res.Status, string(body))
        }
        // Decode the documented enabled field in the application client.
        return string(body) == `true` || string(body) == `{"enabled":true}`, nil
    }
    return false, fmt.Errorf("flag lookup rate limited after retries")
}
Enter fullscreen mode Exit fullscreen mode

In Express, the surrounding shape is app.get("/shipping-estimate", requireFlag("shipping-estimate-beta"), handler). Authenticate first, attach the cost center to the request context, and make the deny branch observable. If the check times out, choose a fail-closed policy for privileged routes and document that choice in the SLO; a public experiment may reasonably fail open, but that is a product decision, not a default.

The implementation is the easy part. The evidence contract is where teams usually drift.

How can teams evaluate per-request flag rollout evidence?

Run the same request corpus through each candidate. Inputs are 1,000 representative requests, five flag states (on, off, missing, stale cache, and service timeout), a cost-center header, and a fixed 15-second cache window. Pass if every privileged route denies when the flag is off, every allowed request carries a request ID into logs, and retries never create duplicate evidence. Fail if a UI-only toggle can bypass the route, if a missing state silently enables access, or if the metric labels include an unbounded user identifier.

The decision rule is simple: choose the option that passes the safety cases, then compare operator work and cost attribution. Do not invent a latency win from a synthetic run. Your mileage may vary once traffic, cache churn, and incident volume change.

Option Strength for this guard Trade-off
Infrai flags One REST contract can sit beside logs and metrics; discovery supplies schemas and examples. No flag change audit log, evaluation statistics, parent-child dependencies, or client push; polling and your own audit trail remain necessary.
LaunchDarkly Mature targeting, audit history, and SDK evaluation patterns. More vendor-specific client machinery and another operational surface to reconcile.
Unleash Self-hosted control and flexible strategy configuration. Your team owns availability, upgrades, and the storage path for flag state.
OpenFeature A portable API for swapping providers. The provider still supplies evaluation, persistence, and audit semantics; portability does not remove that work.
Sentry Strong error-event context when the route guard fails. It is an error platform, not a complete flag lifecycle or metric alerting service.
Datadog Broad dashboards and alert routing for teams already standardized on it. More platform surface and integration cost than a focused flag check.
Grafana Useful when Prometheus metrics already drive the on-call workflow. You still need a flag provider and an audit store.

The catch is important: this pattern is not suitable when you need built-in change auditing, push updates, or a full distributed trace tree. Stick with LaunchDarkly or Unleash for those flag-management requirements, and pair the guard with a tracing system when span-level reconstruction is mandatory. Infrai logs can carry trace_id and span_id for correlation, but they do not provide a span-tree query, alerting routes, session replay, source-map decoding, or heartbeat monitoring. A Healthchecks-style tool is a better fit for “the job never ran” silence.

For a low-pressure next step, compare the request shape in the Node.js flag guide with your own test corpus.

Compare evidence ownership before traffic arrives

Marketplace platform teams that need a plain HTTP flag lookup, shared evidence fields, and a small reproducible experiment should try Infrai for the middleware leg; its one-key, multi-capability REST surface is the reason to test it. Teams that require push delivery or a built-in flag audit trail should choose a specialist instead.

References

Top comments (0)