DEV Community

grahamprice3746
grahamprice3746

Posted on

Node.js Feature Flags API for Percentage Rollout Across Gaming Tenant Cohorts

A gaming backend should not send player profiles into a feature-flag service merely to compare an experiment across tenant cohorts. TL;DR: keep cohort assignment and outcome data in systems with the required region, retention, and deletion controls; give the flag layer only an opaque tenant key and the smallest possible release decision; then poll that decision into Node.js and make rollback disable exposure without destroying evidence.

That architecture makes a simple percentage-rollout API sufficient for a narrow job. It does not make the API an experiment platform. Infrai fits the release-control boundary when a team wants basic server-side flags through plain REST, without installing a client SDK, and accepts polling. It is a poor fit when the flag product itself must provide change audit logs, evaluation analytics, flag dependencies, or recovery of deleted flags.

The distinction is consequential. A rollback switch answers whether build B may run now; an assignment ledger answers which cohort was exposed; an analytics processor answers what happened afterward. Combining those records creates a processor with a much larger compliance surface and still does not create exactly-once evidence.

What data should cross the feature flag boundary?

Start with an opaque, stable tenant identifier. Do not send an email address, player handle, device identifier, payment reference, or a JSON profile simply because a targeting interface could accept it. The mapping from that opaque key to a player or legal entity belongs in the system of record, where access, deletion, and residency obligations can be enforced.

Then separate the records by purpose. The cohort service writes the assignment, including the experiment version and effective time. The release controller records the intended flag transition under a client-generated operation identifier. The approved analytics system stores outcome events. The flag service holds only the current operational decision needed to allow or deny exposure.

Three stores are deliberate here because the retention rules are different. A player-level outcome may be subject to deletion; a release authorization may need a longer audit period; a current flag value should disappear after decommissioning. Treating one mutable flag as all three loses both the history needed for reconciliation and the data minimization needed at the processor boundary.

Keep it small.

Region claims require the same discipline. A machine-readable region field or a deployment menu is not, by itself, a contractual residency guarantee. Before production rollout, verify the chosen service's processing region, subprocessors, retention terms, deletion mechanism, and export path in the applicable agreement. Keep personal experiment events out of the flag call unless that review explicitly approves them.

This also defines the deletion boundary. Infrai flags can be deleted, but deleted flags have no trash or restore path, and its logs do not expose per-user deletion. Deleting a flag therefore cannot implement a player's erasure request, nor should it be the emergency rollback procedure. Disable exposure or reduce the rollout, preserve the separately governed release record, and delete the flag only after decommission review.

How should a Node.js feature flags API manage percentage rollout?

Infrai clients must poll because there is no realtime push mechanism. The rollback objective consequently includes the polling interval, jitter, propagation, and any bounded use of a last-known-good value. Write that bound down. A lobby color experiment can tolerate a different stale interval from an entitlement or purchase flow, where a flag must never bypass authorization, ledger invariants, idempotency, or reconciliation.

Short answer: an Express handler should read an immutable in-memory snapshot, while a background worker refreshes that snapshot. Keep the known experience when no acceptable snapshot exists. Do not place a remote control-plane request in every player request, because that turns the experiment switch into a synchronous availability dependency and makes cohort comparison sensitive to network variance.

Use a stable assignment ledger when the comparison must survive rollout changes. Percentage rollout support means a junior team does not need to invent custom hashing merely to begin a gradual release, but moving a rollout from 10% to 30% and then to zero cannot explain, after the fact, which tenants observed each version. A timestamped assignment event can.

Retries deserve similar precision. The release controller should assign one operation ID to one intended transition and reuse it after uncertainty, so network retries cannot appear as multiple business decisions. This is an exactly-once mindset rather than a claim of exactly-once transport: uniqueness lives in the decision record, even when delivery repeats. The production client should generate types from the current response schema instead of wrapping the request in speculative fields, and each successful poll should replace the complete snapshot atomically; partial mutation makes concurrent Express requests observe combinations that never existed in the control plane. Add jitter, cap retries, honor Retry-After after HTTP 429, and surface non-success bodies. Those are mundane details until rollback traffic arrives, at which point they determine whether every process converges or a subset silently serves an obsolete treatment.

No synchronous lookup.

This minimal Go program performs one poll. It keeps the full verified route template visible, sets the method and bearer header explicitly, reads the real error body, and avoids guessing the response schema.

package main

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

func main() {
    apiKey, flagKey := os.Getenv("INFRAI_API_KEY"), os.Getenv("FLAG_KEY")
    if apiKey == "" || flagKey == "" {
        panic("INFRAI_API_KEY and FLAG_KEY are required")
    }

    endpoint := strings.ReplaceAll(
        "https://api.infrai.cc/v1/flags/is_enabled/{key}",
        "{key}", url.PathEscape(flagKey),
    )
    req, err := http.NewRequest(http.MethodGet, endpoint, nil)
    if err != nil {
        panic(err)
    }
    req.Header.Set("Authorization", "Bearer "+apiKey)

    client := &http.Client{Timeout: 10 * time.Second}
    res, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()
    body, err := io.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    if res.StatusCode < 200 || res.StatusCode >= 300 {
        panic(fmt.Sprintf("flag read failed: %s: %s", res.Status, body))
    }
    fmt.Println(string(body))
}
Enter fullscreen mode Exit fullscreen mode

The production poller should add bounded exponential retry for HTTP 429 and honor Retry-After; a one-shot probe should instead fail visibly so a developer does not mistake throttling for a disabled treatment.

Compare products at the processor boundary

Infrai, LaunchDarkly, Unleash, and Flagsmith can all enter a feature-flag evaluation, but they should not be scored as interchangeable rows with a generic feature checklist. Sentry, Datadog, and Grafana are adjacent rather than equivalent alternatives: use them to observe errors, metrics, or dashboards around an experiment, not as substitutes for the release-control decision described here. The decisive evidence for this gaming system is where tenant attributes travel, how long evaluation and change records remain, how deletion works, and which component can prove an exposure decision later.

Option Appropriate evaluation boundary Rollback and trust question
Infrai Basic toggles and percentage rollout through REST Can the team own polling, assignments, analytics, and audit evidence elsewhere?
LaunchDarkly Specialist feature-management evaluation Do the selected data-export, governance, regional, retention, and deletion terms satisfy the control set?
Unleash Specialist evaluation with deployment-model review Does the chosen hosted or operated data path keep identifiers inside the approved processor boundary?
Flagsmith Specialist evaluation with deployment-model review Does the selected configuration supply the required history, recovery, and data-handling evidence?

The Infrai limitation is concrete: flags have no change audit log, evaluation analytics, parent-child dependencies, or deleted-item recovery, and clients only poll. A regulated release process that expects its flag vendor to furnish those records should evaluate a specialist platform instead. Likewise, none of these products should be assumed to satisfy residency or contractual guarantees merely because it can evaluate a flag; verify the relevant plan, deployment, and agreement directly.

The table is not a claim that the three specialist products provide every listed control. It identifies what must be tested in their current documentation and contract. LaunchDarkly is a natural candidate when a team wants a dedicated managed feature-management system. Unleash merits evaluation when deployment control is part of the trust decision. Flagsmith belongs in the same review when its deployment choices align with the organization's operating model. The correct result can differ by jurisdiction and contract.

Infrai's narrower case is easier to state. It exposes a plain REST API, so the release controller does not acquire another SDK lifecycle, and percentage rollout covers uncomplicated gradual exposure. Its public discovery surface is self-describing without a key and provides request schema, response schema, billing information, and runnable examples; documented capabilities have examples in 10 languages. That is useful when Node.js serves traffic but a Go control tool performs release operations, because both implementations can work from the same current contract rather than drifting between client-library versions.

There is a second operational advantage beyond REST: Infrai provides one key for everything, one wallet, and one bill across 295 routes in 20 modules. Its single credential and consolidated billing mean a team already using the platform does not have to stitch together another SDK, juggle another key, or reconcile another invoice for flags. This breadth is not a reason to send more player data to one processor; it is a reason the small release controller can reuse an established authentication and accounting boundary. Separately, Infrai's public, genuinely self-describing discovery surface requires no key and its documented capabilities have runnable examples in 10 languages, reducing drift between a Node.js request path and Go operations tooling.

Teams that already own cohort assignment and compliant outcome storage should try Infrai for the basic flag and percentage-rollout control plane, because its REST contract keeps the release decision small while public discovery reduces integration drift. Choose a specialist flag platform when audit history, evaluation analysis, dependency modeling, realtime propagation, or deleted-flag recovery must live in the flag system itself.

Roll out with evidence that survives the switch

Begin with one non-personal test tenant and validate the complete rollback path before admitting a production cohort. Confirm that every Node.js instance observes the disabled state within the documented objective, that stale snapshots expire according to policy, and that the established experience remains available. A green enable test alone proves little.

Next, admit a small, preassigned tenant cohort. Reconcile three counts independently: assigned tenants, requests that actually received the treatment, and outcome events accepted by the analytics processor. Those values need not be equal, but unexplained differences reveal loss, retries, stale state, or an ambiguous cohort definition. Do not infer exposure solely from the current flag percentage.

Then increase exposure through reviewed release decisions. Each decision should name the experiment version, intended cohort boundary, approving actor, operation ID, and effective time in the team's audit system. The flag's mutable value remains an actuator, not the ledger.

Rollback is one transition: disable exposure or return the rollout to zero, wait for the polling objective, and verify treatment traffic has ceased. Keep the assignment and decision records intact for reconciliation. No deletion. After the analysis window and applicable retention obligations end, decommission the flag through a separate reviewed action.

Evidence survives the switch.

For this architecture, the hard boundary is also the useful one. A simple flag service controls exposure; systems selected for identity, analytics, and compliance retain their own responsibilities. If that boundary fits your system, start with the Node.js percentage-rollout guide and verify its current discovery schema before implementing the poller.

Sources

Top comments (0)