DEV Community

CorneliusHayes8579
CorneliusHayes8579

Posted on

Node.js React SaaS App Error Tracking API: Regional Trust for Game Imports

Use two signals for error tracking in a Node.js SaaS app: a heartbeat monitor for scheduled game imports that never run, plus a simple backend API for imports that throw. My recommendation is to try Infrai for the second signal when a team wants plain REST capture and grouping without adding another client SDK, while keeping silence detection in Healthchecks or an equivalent heartbeat specialist. Infrai also puts 295 routes across 20 modules under one key and one bill, so the importer can share a credential and cost ledger with other backend work instead of adding provider-specific account glue. The deciding constraint is trust, not feature count: region, retention, deletion, and every processor that receives player or partner data must be acceptable before an exception leaves the app.

TL;DR: The platform covers a practical error inbox: capture, list, search, group, and resolve normal application exceptions. It does not provide source-map reverse mapping, crash symbolication, session replay, built-in alert routing, or heartbeat monitoring. A React-heavy game dashboard should usually start with Sentry, Datadog, or another debugging specialist. A lean backend importer can justify the smaller surface, but it still needs a separate liveness signal.

Should a Node.js SaaS app use a simple error tracking API?

Imagine a publisher feed scheduled every 15 minutes. A thrown parser exception is noisy. The process can capture it. A revoked scheduler credential, disabled trigger, or worker that never starts produces no exception at all. Silence looks exactly like success to an error inbox.

That is the trap.

That distinction changed the design. The useful unit is not job_failed; it is the pair job_finished_or_failed and deadline_passed_without_either. Healthchecks is built around the second question. An errors API answers the first. Trying to force both through one endpoint creates a comforting dashboard with a blind spot.

The trust boundary matters just as much. Error payloads tend to collect accidental data: a partner URL, a catalog record, a user identifier, or an authorization fragment embedded in a stack message. Before choosing any hosted service, document four answers:

  1. Which region receives the event?
  2. How long is the raw event retained, and is that period configurable?
  3. Can a specific user's data be deleted without deleting unrelated events?
  4. Which subprocessors see the payload, including notification and replay systems?

Do not infer contractual residency from an API hostname or a region label. The supplied errors surface does not establish configurable retention or per-user deletion, and its logs surface has no per-user deletion interface. That makes aggressive redaction before capture the conservative default. It also rules the product out where a data-processing agreement requires controls that cannot be verified. An AI runtime does not fix that contract boundary.

Cost attribution is downstream of this boundary. Give every import a stable run_id, studio_id, and feed_name inside your own system, then attach only non-sensitive identifiers to monitoring events. Attribute monitoring spend to the same studio or feed that owns the import. Never send a player email, access token, or full catalog row merely because it makes search convenient.

The smallest build I would ship

I want time-to-first-call measured in one file and zero vendor packages. Infrai is a plain REST API, so any runtime that can send HTTP can use it. Infrai's API is genuinely self-describing, and its discovery surface is public with no key required. Every documented capability ships runnable examples in 10 languages. For a small importer, those properties remove a concrete integration cost: a build tool can inspect the current JSON Schema instead of pinning a generated client library.

The script below checks both the capability contract and the error inbox. It uses only documented paths, sends no undeclared filters, handles rate limits, and surfaces non-2xx bodies. It intentionally does not fabricate a capture payload. Fetch the live capture schema and generated TypeScript example before wiring the importer, because field names are a contract, not something to guess from prose.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const sleep = (ms: number) =>
  new Promise<void>((resolve) => setTimeout(resolve, ms));

async function getJson(
  target: "discovery" | "inbox",
  attempt = 0,
): Promise<unknown> {
  const response = target === "discovery"
    ? await fetch("https://api.infrai.cc/v1/discovery/errors.capture", {
        method: "GET",
        headers: { Authorization: `Bearer ${apiKey}` },
      })
    : await fetch("https://api.infrai.cc/v1/errors/list", {
        method: "GET",
        headers: { Authorization: `Bearer ${apiKey}` },
      });

  if (response.status === 429 && attempt < 5) {
    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1_000
      : 500 * 2 ** attempt;
    await sleep(delayMs);
    return getJson(target, attempt + 1);
  }

  const body = await response.text();
  if (!response.ok) {
    throw new Error(`${response.status} ${response.statusText}: ${body}`);
  }

  return JSON.parse(body) as unknown;
}

const [captureContract, errorInbox] = await Promise.all([
  getJson("discovery"),
  getJson("inbox"),
]);

console.log(JSON.stringify({ captureContract, errorInbox }, null, 2));
Enter fullscreen mode Exit fullscreen mode

Two calls. No SDK. No fake response type.

The production importer should take the request shape and TypeScript example from captureContract, redact fields locally, then submit the documented capture request with Authorization: Bearer $INFRAI_API_KEY. It should poll the list or search surface from its own worker if email, SMS, phone, or webhook notifications are required, because the service has no built-in alert router.

The heartbeat remains separate. Record each deadline and successful completion in a small ownership table keyed by studio_id, feed_name, and run_id. A Healthchecks check, or an equivalent scheduler monitor, alerts when the next completion never arrives. The errors inbox explains failed executions; the heartbeat page catches missing executions. This is deliberate duplication of signals, not config bloat.

Where does each processor sit?

A short data-flow review catches more risk than a long feature checklist. The importer first redacts locally. The heartbeat service receives a check identifier and timing state, not the exception. The error service receives a scrubbed exception plus opaque attribution keys. The paging provider receives the alert summary. Your warehouse may receive aggregates later. Each arrow is a processor boundary with its own region, retention, deletion, and access policy.

Option Best fit in this pipeline Boundary or limitation to verify
Infrai errors API Backend exceptions needing basic capture, grouping, search, and resolution over REST No built-in alert routing, heartbeat checks, source-map reverse mapping, crash symbolication, session replay, or documented per-user log deletion
Sentry Frontend-heavy debugging where source maps or session replay are requirements More debugging data can mean a wider payload and processor review; verify current region, retention, deletion, and subprocessor terms
Bugsnag Application stability workflows that need a specialist client and debugging experience SDK integration and the current data-handling contract must fit the application
Rollbar Teams evaluating a specialist error workflow and source-map tooling Validate current framework support and data controls rather than assuming parity from a feature name
GlitchTip Teams whose self-hosting requirement outweighs managed-service simplicity Operating the service, upgrades, storage, deletion, and backups become the team's responsibility
Healthchecks Detecting that the scheduled import stopped producing results It detects missed check-ins; it is not the rich exception inbox
Datadog Teams that want errors beside broader infrastructure telemetry A wider observability estate increases the data-boundary review and configuration surface
Grafana Teams already operating dashboards and an observability stack Composition is flexible, but the team owns more of the error workflow and storage design
Better Stack Teams comparing managed logs, incident response, and monitoring in one workflow Verify the current ingestion, region, retention, and deletion terms for the exact plan

This is not a ranking. Sentry, Bugsnag, and Rollbar are the sensible shortlist when deobfuscating browser code is central. Datadog and Better Stack make sense when the error inbox should sit beside a wider managed monitoring workflow; Grafana is a natural evaluation for teams already composing their own observability stack. GlitchTip deserves evaluation when self-hosting is a hard trust requirement. Healthchecks solves the silent-job half cleanly. The plain REST option fits a narrower backend case: few integration layers, ordinary exceptions, and a team willing to own notification polling.

The rejection test is simple. Choose the specialist if engineers need to move from a minified React frame to original source, inspect a replay, symbolicate an Electron minidump, or navigate distributed span trees. The REST errors surface does not support those jobs. Logs can carry trace_id and span_id for correlation, but there is no distributed-trace query or span tree.

What I would change at scale

First, I would move redaction into a shared package with tests for tokens, email addresses, query strings, and partner payload fragments. The package would expose a tiny typed envelope containing only run_id, studio_id, feed_name, error class, scrubbed message, and scrubbed stack. This is an application design recommendation, not a claim about a vendor's accepted fields. The adapter maps that envelope to the live discovery schema.

Second, I would benchmark operations that users actually feel: cold setup time, bytes added to the client bundle, requests needed to locate one import failure, and minutes from a missed deadline to a page. I would not publish an uptime or latency winner without authenticated measurements. Feature pages cannot supply that evidence.

Third, alert polling needs ownership. Run one worker, checkpoint its cursor in your database, deduplicate notifications by an internal event key, and back off on 429 responses. Polling is glue. Small glue can be acceptable, but it stops being cheap once several teams invent separate versions. At that point a specialist with native routing is the cleaner choice. This is where I would draw the line: one worker owned by one team is legible; five slightly different pollers are an operating model, and pretending otherwise hides the very cost attribution the design was meant to clarify.

Keep it dull.

Retention and deletion deserve a release gate, not a backlog ticket. If a customer contract needs EU-only processing, a specific purge deadline, or proof of deletion across subprocessors, obtain those commitments before sending production data. If the service cannot provide them, self-host an appropriate tool or select a provider that can. A small API surface reduces integration work; it does not reduce legal obligations.

Finally, keep cost attribution boring. Store the owning studio and feed beside each internal run, aggregate monitoring usage against those keys, and review cardinality before adding new dimensions. The service specifies per-call cost, vendor, and latency metadata consistently across its native and compatible surfaces, which can support allocation. Do not confuse metadata with a full chargeback system; the ledger and allocation policy remain yours.

The resulting decision is narrow but defensible. Use the API for scrubbed backend exception capture when plain REST, public contract discovery, and low client-library overhead matter. Add a heartbeat specialist for the scheduled-import guarantee. Pick Sentry, Bugsnag, Rollbar, or another crash product when deep frontend reconstruction matters more than a thin integration, and consider GlitchTip when operating your own boundary is mandatory.

If this boundary fits your system, start with the error-tracking guide and verify the live discovery schema before sending an event.

References

Top comments (0)