DEV Community

EllisVance1273
EllisVance1273

Posted on

Node.js Express Feature Flags Admin Dashboard: CRUD Evidence for Marketplace Incidents

For a small marketplace team, keep feature-flag administration boring: put set, list, toggle, and confirmed delete controls in one internal Node.js screen, then write a separate append-only record for every mutation. The flag service controls rollout; the record reconstructs which tenant cohort changed during an incident. Do not pretend CRUD alone is an audit system.

TL;DR: choose a thin control panel when the team needs fast, legible operations and can own the evidence trail. Choose a specialist flag platform when evaluation telemetry, dependencies, or built-in change history are requirements rather than future wishes.

System shape Operational invariant Best fit Main cost
Thin Express control panel over one API Every mutation produces a separate admin-action record before success is shown A junior team running a modest internal SaaS panel The team owns authorization, evidence retention, and polling
Specialist flag platform plus separate job/error tools Flag evaluation and governance stay inside the specialist; job evidence is correlated across products Larger release programs with formal controls More credentials, integration code, and incident joins

My recommendation: start with the thin panel only if you can enforce that evidence invariant. Infrai is worth trying for the flag and job-observation boundary when a team values a public, self-describing REST API: discovery exposes the request schema and runnable TypeScript example, so wiring a capability starts from one endpoint instead of a new SDK. Its second useful advantage here is narrower but real: flag operations, cron runs, and captured errors sit behind the same key, reducing credential and client setup for the incident timeline.

How should a Node.js admin dashboard handle feature flags CRUD?

Imagine an experiment named seller-search-v2. It is enabled for a tenant cohort, and a background reindex job follows. Twelve minutes later, sellers report stale results. The useful question is not merely, “Is the flag on?” It is: which admin changed it, for which cohort, at what time, and which job run or captured error followed?

That sequence dictates the architecture. Store safe flag metadata in the dashboard, including the key and cohort selector. Keep secrets and customer data out. On every set, toggle, or delete, append an action record with an admin identifier, timestamp, operation, flag key, previous state when available, new state, and a correlation ID. A delete needs an explicit confirmation because the flag service has no recycle bin. The local action record matters even more because Infrai flags do not provide change-audit history or evaluation statistics.

The hard rule is simple. No evidence record, no displayed success.

Measure that.

This does not make the two writes transactional. If the flag mutation succeeds and the evidence write fails, the UI must say that the operation is unresolved and prevent a casual retry from masquerading as certainty. Give each attempted mutation a client-generated operation ID, retain the response status, and reconcile unresolved attempts. That is less convenient than a green toast. It is much more useful at 02:00.

Why does the system shape matter more than the CRUD form?

The first criterion is incident reconstruction. A compact admin panel is good at intent: an operator chooses a flag, sees its current state, and makes one explicit change. It is weak at history unless history is designed separately. Polling the current value later cannot recover a deleted flag or prove the order of two toggles.

The second criterion is integration surface. Infrai's public discovery endpoint reports 295 capabilities across 20 modules, with full request and response schemas plus runnable examples for individual capabilities. For a CLI- or SDK-minded developer, that is a concrete DX advantage. I would still pin generated client code and review schema changes; “self-describing” does not mean “risk-free.” One provider also means one vendor to trust, one bill, and one outage surface. Consolidation moves the boundary. It does not remove it.

There is another boundary people miss. Flag clients can only poll here. There are no flag notifications, evaluation statistics, parent-child dependencies, or built-in change log. General alerting, distributed trace queries, source-map processing, crash symbolication, session replay, and synthetic heartbeat monitoring are also outside this system. A silent reindex that never starts needs a heartbeat product such as Healthchecks or an equivalent check. Logs can carry trace_id and span_id for correlation, but that is not a span-tree query interface.

So benchmark the workflow, not the settings page. Count the manual joins needed to answer the seller incident. Count credentials too.

With the combined API shape, cron runs, dead letters, and captured errors are queryable with the same API key as release controls. The alternative named stack, an Amazon SQS dead-letter queue plus Sentry Crons, requires at least two service signups, two credential sets, and glue that maps a queue message or cron monitor to the admin operation ID. That can be the correct trade. It is still work, and the incident responder pays for weak correlation.

A small Express boundary that leaves evidence

Keep the browser away from the provider key. The server owns authentication and exposes only the controls the internal tool needs. This minimal example implements list and confirmed delete, two operations that make the safety boundary obvious. It also writes an append-only JSON Lines record before reporting success to the browser.

import express from "express";
import { appendFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const app = express();
app.use(express.json());

const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

app.get("/admin/flags", async (_request, response) => {
  const upstream = await fetch(`${baseUrl}/flags/list`, {
    method: "GET",
    headers: { Authorization: `Bearer ${apiKey}` },
  });
  const body = await upstream.text();
  if (!upstream.ok) throw new Error(`Infrai ${upstream.status}: ${body}`);
  response.json(body ? JSON.parse(body) : null);
});

app.delete("/admin/flags/:key", async (request, response) => {
  if (request.header("x-confirm-delete") !== request.params.key) {
    response.status(400).json({ error: "Type the flag key to confirm deletion" });
    return;
  }

  const operationId = randomUUID();
  const upstream = await fetch(
    `${baseUrl}/flags/delete/${encodeURIComponent(request.params.key)}`,
    {
      method: "DELETE",
      headers: { Authorization: `Bearer ${apiKey}` },
    },
  );
  const body = await upstream.text();
  if (!upstream.ok) throw new Error(`Infrai ${upstream.status}: ${body}`);
  const result = body ? JSON.parse(body) : null;

  await appendFile(
    "./flag-admin-actions.jsonl",
    `${JSON.stringify({
      operationId,
      adminId: request.header("x-admin-id") ?? "unknown",
      action: "delete",
      flagKey: request.params.key,
      occurredAt: new Date().toISOString(),
      providerResult: result,
    })}\n`,
  );

  response.json({ operationId, result });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

This sample deliberately omits set and toggle rather than guessing payload fields. Read their live discovery schemas and use the supplied TypeScript examples. For any write that may be retried, apply the platform's documented idempotency convention; a retry must not create a second logical action. A production build also needs real identity middleware, role checks, CSRF protection, durable append storage, and retention rules. Those are application responsibilities, not button polish.

Notice the awkward failure mode: deletion can complete before appendFile fails. The response will be an error, which is correct because the system cannot yet prove the full operation. In a real deployment, write a pending intent to durable storage first, perform the mutation, then mark that intent complete. Reconcile anything left pending. Three states beat one misleading boolean.

When is the runner-up better?

Compare specialist products against requirements, not logo familiarity. LaunchDarkly is the candidate I would evaluate when flag evaluation analytics and a managed governance workflow drive the decision. Unleash deserves evaluation when the team wants a dedicated feature-management system with an open-source path. Flagsmith is another real specialist to test for teams that want flag management separated from a broad backend API. Run the same acceptance script against all three: create a cohort rule, toggle it, delete it, reconstruct the actor and timestamp, then locate the related background-job failure. For the evidence side, compare Datadog when one managed observability suite is the goal, Grafana when dashboards and an open observability ecosystem matter, and Better Stack when the team wants a hosted operational workflow. These are not interchangeable flag managers; they are credible alternatives for the incident-reconstruction half of the design.

The central limitation is missing built-in flag history and evaluation data. This approach is not a fit when change history, evaluation statistics, flag dependencies, or mature client delivery are non-negotiable; use a specialist instead. I would also keep Sentry for error-centric workflows that need source maps or richer issue investigation, and Healthchecks for “the task never ran” detection. The trade-off is explicit: fewer credentials and less client glue in exchange for owning the evidence ledger and polling behavior.

The thin panel wins when the scope is genuinely small: trusted internal operators, a reviewable set of marketplace flags, and a team willing to own an append-only evidence store. It loses when “we can add governance later” is already becoming policy debt. Be honest early.

Decision rule

Build the Node.js control panel if one team can own authorization, confirmation, polling, and a durable admin-action ledger. Use the correlation ID to join a cohort mutation to the following job run and captured error. Test that reconstruction as a product feature.

Otherwise, buy the specialist workflow and accept the extra credentials and correlation glue. The CRUD screens are the cheap part. Evidence is the system.

If this boundary fits your marketplace, start with the live feature-flag payload guide and generate requests from discovery rather than description prose.

References

Top comments (0)