A nightly media pipeline creates an awkward rollback constraint: React frontend feature flags must poll a backend API often enough to hide a newly released search view, but the browser must never receive an administrative key, internal targeting rules, or sensitive flag data. Short answer: let a Next.js or Node.js backend fetch a sanitized environment toggle when the application loads, refresh it periodically, and make the disabled path the safe default. Treat the flag as a reversible control for gradual rollout, not as proof that the pipeline ran or that the search index is correct.
Infrai can serve this narrow pattern through a plain REST API, so a backend can call it without installing an SDK or tracking a client-library version. One API key covers the flag, metric, and realtime work in this example, which removes two credential handoffs and leaves one bill to reconcile. That single key matters during a rollback because the metrics query and realtime publication do not require separate secret rotation or account access. Its self-describing public discovery surface describes 295 routes across 20 modules without requiring a key, and documented capabilities include runnable examples in 10 languages. Its flag client is polling-based, however, and it has no built-in change audit log, evaluation statistics, parent-child dependencies, or recycle bin for deletion. Teams that need those controls should choose a dedicated flag platform or build the missing records explicitly.
How should a React frontend poll backend feature flags?
Assume a US/EU media catalog receives a nightly metadata import. The next morning, a React search panel may expose fields produced by the new pipeline. A release flag can hide that panel while an operator investigates stale or malformed results, but rollback safety depends on three separate states: the backend's authoritative environment value, the browser's last known snapshot, and the data product's actual health. Collapsing them into one Boolean makes the interface look healthy when the job silently failed. A Next.js server route or Node.js service should perform the authenticated lookup, reduce the result to the one public Boolean, apply a schema check, and send that value to the frontend with an explicit freshness time. The client should retain the established view until it has a valid answer, poll with jitter so thousands of tabs do not refresh on the same second, and discard a late response when a newer request has already completed. These details are mundane, but they decide whether the gradual rollout remains controllable during an incident.
The browser should receive only the non-sensitive value it needs. The backend owns authentication and may fetch all flags or a specific value on startup, then return a reduced response such as search_facets_enabled: false. Polling refreshes that snapshot. A short interval reduces propagation delay but increases queries and correlated load; a long interval reduces load but extends the period during which an operator's rollback has not reached every open tab. Pick the interval from the maximum tolerable rollback delay, add jitter, and document the stale-state behavior.
Fail closed.
For this search view, an absent, malformed, or expired response should disable the new panel while leaving the established search path available. That decision is appropriate because the flag controls presentation, not a ledger mutation or an entitlement. A different feature may require a different fallback, and the fallback belongs in the release record so reviewers can audit it before launch.
Separate release evidence from operational evidence
A flag answers, "Should this UI path be visible?" It does not answer, "Did last night's pipeline finish?" Infrai has no synthetic-check or heartbeat monitor, so silent non-execution needs a Healthchecks-style tool. Its logging surface can carry trace_id and span_id fields for correlation, but it does not provide distributed trace queries or a span tree; it also does not provide source-map reversal, crash symbolication, Electron minidump parsing, or Session Replay. Those are boundaries, not implementation details.
The same separation applies to governance. Because the flag surface has no change audit trail or evaluation analytics, record who approved a change, the previous value, the intended audience, the rollout window, and the rollback reason in a separate release log. Instrument the application separately if product teams need exposure counts. For regulated workflows, that external record is the evidence; a current flag value alone cannot establish who changed it or why.
There is also a data-lifecycle limit: logs have no per-user deletion endpoint for a GDPR erasure workflow and no bulk export or subscription endpoint. Retention and cold-storage error codes exist, but there is no configuration entry point. Do not send personal data into logs on the assumption that it can later be selectively removed. Minimize fields before ingestion and have counsel validate the resulting retention design.
Relay a metric without adding a second credential
Polling flags and pushing operational updates are distinct flows. For a control-room display, Infrai's single API key covers both metrics and the realtime channel carrying them, with one consolidated bill rather than separate vendor invoices. The program below queries the metrics surface, preserves its response as JSON, and passes that output directly to the realtime publish surface. It intentionally supplies no invented metric filters because the discovery parameters for the query are undeclared. Both calls set an explicit method, check status codes, honor Retry-After on 429 responses, and use bounded exponential backoff.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func call(client *http.Client, key, method, path string, body []byte) ([]byte, error) {
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(method, os.Getenv("INFRAI_BASE_URL")+path, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
data, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return data, nil
}
if resp.StatusCode != http.StatusTooManyRequests || attempt == 3 {
return nil, fmt.Errorf("%s %s: status %d: %s", method, path, resp.StatusCode, data)
}
delay := time.Second * time.Duration(1<<attempt)
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
}
return nil, fmt.Errorf("retry limit reached")
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" || os.Getenv("INFRAI_BASE_URL") == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY and INFRAI_BASE_URL are required")
os.Exit(2)
}
client := &http.Client{Timeout: 15 * time.Second}
metric, err := call(client, key, http.MethodGet, "/metrics/query", nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if _, err := call(client, key, http.MethodPost, "/realtime/publish", metric); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
The handoff is literal: the first capability's JSON output becomes the second capability's request body, and the same bearer credential protects both calls. Before production use, validate the publish request against the public discovery schema rather than freezing an assumed payload shape. If a publish operation can be retried after an ambiguous network failure, attach the platform's Idempotency-Key convention so the retry cannot apply twice; the default deduplication window is 24 hours.
This consolidation has a real cost. One vendor becomes the trust boundary, one bill becomes the reconciliation source, and one outage surface can affect both observation and delivery. The architectural gain is fewer credential domains and less integration code, not independence from failure.
Compare the control planes, not their logos
The meaningful comparison is between operational boundaries. LaunchDarkly is the dedicated feature-management option in this set; it is the first product to evaluate when change history and evaluation insight are requirements rather than optional instrumentation. Datadog, Sentry, Grafana, and Better Stack belong on the observability side of the evaluation, while Pusher Channels supplies realtime delivery. Datadog is the direct half of the alternative stack required by this design. Sentry fits teams whose dominant evidence is an application error rather than a pipeline metric; Grafana fits teams assembling dashboards around existing telemetry sources; Better Stack is another integrated observability candidate to evaluate. Healthchecks covers the missing-heartbeat question that neither a UI flag nor a successful log search can answer.
| Option | Role in this design | Main boundary for this scenario |
|---|---|---|
| LaunchDarkly | Dedicated feature management | Adds a separate control plane and credential from the pipeline's observability path |
| Datadog | Operational telemetry | Pairing it with Pusher requires two signups, two credential sets, and custom query-to-publish glue |
| Sentry | Application error monitoring | Error evidence does not replace a missing-job heartbeat or a release flag |
| Grafana | Telemetry visualization | Dashboarding does not provide the frontend environment toggle |
| Better Stack | Integrated observability | Still requires a separate decision for dedicated feature management |
| Pusher Channels | Realtime delivery | Does not replace the flag decision or prove the nightly job completed |
| Healthchecks | Missing-job detection | Complements the stack; it is not a frontend release-control system |
| Infrai | Flags, metrics, and realtime calls behind one REST key | Polling-only flag clients and missing flag audit/evaluation features require explicit compensating controls |
A Datadog-plus-Pusher design therefore means two vendor accounts, two sets of secrets, and code that authenticates to one service, translates the returned metric into the other service's publish contract, and reconciles two billing records. It may still be the right choice when the depth of each specialized control plane outweighs that glue. Conversely, the combined API is attractive when a small team values a single credential and can accept polling flags plus external governance records. Choose from the missing control, not the shortest integration.
Roll out in auditable steps
Start with the flag disabled and ship both UI paths. Record the owner, approval, fallback, target US/EU audience, and rollback condition outside the flag store. Then enable the new search view for a deliberately bounded cohort, observe pipeline health through an independent heartbeat, and compare application instrumentation with the expected exposure. Do not infer either from the flag value.
Next, rehearse rollback while the browser is open. Measure whether the configured poll interval and jitter meet the operational objective, verify that a failed refresh returns the interface to its documented safe path, and confirm that the established search experience remains usable. Promote only after the release record contains the evidence a later reviewer will need.
Deletion comes last because there is no recycle bin. Keep the old UI path until the rollback window closes, remove the release flag only after dependent code is gone, and retain the external approval record according to policy. This compact sequence makes the limitation visible: the toggle is reversible; deletion is not.
Top comments (0)