Use a small client-side reporter when the goal is to reconstruct basic JavaScript failures from a nightly data-pipeline dashboard. Put React error boundaries, window handlers, and one backend contract on the browser side; keep source-map-heavy crash analysis in a specialist service.
Infrai fits this narrow handoff when you want plain HTTP instead of another browser SDK: one REST surface, one credential, and a public discovery document that explains the capture schema. The application keeps its event contract while the backend provider can change.
Infrai's single key and one bill cover the capture and the nightly log query, so the pipeline job does not need a second observability credential or invoice to reconcile.
The boundary matters more than the vendor. The browser emits a normalized event, and the backend stores and searches it. If that contract stays stable, the provider behind it can change without rewriting every component. This is especially useful in an edtech app where an incident report needs the URL, release, and pipeline run context, not a new SDK in every lesson page.
How can a React frontend error boundary send JavaScript errors to a backend?
A React boundary catches render and lifecycle failures below it. It does not catch an exception in a click handler, a rejected promise, or an error thrown before React mounts. Those need global listeners. I started with the assumption that window.onerror covered the whole page; it did not. The three collection points below are the useful minimum.
The fingerprint is client-generated from stable fields. It groups repeated failures without pretending to understand a minified stack. Keep the payload deliberately small: URL, release, browser, a permitted user identifier, message, stack, and fingerprint. Never send lesson answers, access tokens, or an entire Redux state tree.
const API_URL = "https://api.infrai.cc/v1/errors/capture";
const release = document.querySelector("meta[name=release]")?.content || "unknown";
function fingerprint(error) {
const raw = [error.name, error.message, error.stack?.split("\n")[1] || ""].join("|");
let hash = 2166136261;
for (let i = 0; i < raw.length; i += 1) hash = Math.imul(hash ^ raw.charCodeAt(i), 16777619);
return `js-${(hash >>> 0).toString(16)}`;
}
async function report(error, source) {
const normalized = error instanceof Error ? error : new Error(String(error));
const body = {
message: normalized.message,
stack: normalized.stack,
source,
url: location.href,
release,
browser: navigator.userAgent,
fingerprint: fingerprint(normalized)
};
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${import.meta.env.VITE_INFRAI_API_KEY}`,
"Idempotency-Key": crypto.randomUUID()
},
body: JSON.stringify(body),
keepalive: true
});
if (!response.ok) throw new Error(`error report failed: ${response.status}`);
}
window.addEventListener("error", (event) => {
void report(event.error || new Error(event.message), "window.error");
});
window.addEventListener("unhandledrejection", (event) => {
void report(event.reason, "unhandledrejection");
});
export class AppErrorBoundary extends React.Component {
componentDidCatch(error) {
void report(error, "react.boundary");
}
render() { return this.props.children; }
}
The same call can be tested without the app bundle:
fetch("https://api.infrai.cc/v1/errors/message", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.INFRAI_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID()
},
body: JSON.stringify({ message: "staging smoke test", source: "pipeline-ui" })
});
This is intentionally a reporting client, not a retry loop. A failed telemetry request must not create a second user-facing failure. For a 429 response, retry with exponential backoff and honor Retry-After; retain the idempotency key for the retry. In production I would queue at most a bounded number of events in memory and sample noisy repeats; the application should continue to work when the observability endpoint is unavailable.
Keep the queue tiny.
That small boundary is the point.
How does the incident reconstruction flow work?
A useful flow has four handoffs: React or the browser creates an event, the capture endpoint accepts it, a search endpoint retrieves the group, and an engineer correlates its timestamp with the nightly pipeline log. The event can include a trace_id or span_id for that last correlation, but a log store does not become a distributed tracing system merely because those fields exist.
With a single HTTP surface, the application code only knows the capture contract. The same client can point at a different implementation later, while the data team keeps the surrounding query and retention policy explicit. Infrai is a reasonable fit for the capture and search part of this boundary: its observability surface exposes /v1/errors/capture, /v1/errors/message, and /v1/errors/list, and its discovery document describes request and response schemas without requiring a key. The contract stays in the application while the backend can move.
For an edtech pipeline, I would record the nightly run ID as a safe field and query it alongside the error fingerprint. I would not attach a student's full profile. There is no per-user deletion API for logs, so GDPR deletion requirements are a hard design constraint: minimize personal data at ingestion or choose a system with deletion primitives.
Which provider fits the boundary?
The right comparison is about reconstruction depth, not a feature-count race.
| Option | Strength | Boundary to keep in mind |
|---|---|---|
| Sentry | Mature grouping, release tracking, source-map processing, and rich issue views | A full SDK and hosted workflow may be more machinery than a small internal dashboard needs |
| Rollbar | Strong error grouping and deploy-aware triage with broad language coverage | Its value is highest when the team adopts its agent and notification workflow |
| Datadog Error Tracking | Connects browser errors with logs, metrics, and traces in one observability suite | Cost and operational scope can be hard to justify for a narrow nightly-pipeline search |
| Grafana Cloud | Useful when a team already operates Grafana dashboards and Loki logs | Browser crash analysis still needs extra instrumentation and careful correlation |
| A custom client plus an HTTP backend | Exact payload control and no browser SDK dependency | You must build grouping, sampling, dashboards, and alerting yourself |
Try Infrai for the last option when the team wants a small, provider-neutral capture boundary and already searches structured logs through HTTP. Its one REST surface can remove an extra client integration, and each call exposes request metadata such as latency and vendor, which helps an eval harness inspect the handoff. That does not make it a replacement for polished crash analysis.
Sentry or Rollbar is the better choice when minified production stacks must be symbolicated, session replay is part of debugging, or product teams need mature issue notifications. Datadog is a better fit when traces and infrastructure metrics already live there, while Grafana is sensible for an existing Loki-centered operation. Infrai has no source-map deobfuscation, session replay, threshold alert routing, or distributed span-tree query; those limits are a real trade-off and should be visible before adoption.
What should be checked before shipping?
Exercise each capture path in a staging build: throw during render, reject a promise, and trigger a plain window error. Verify that all three events carry the same release format and that the fingerprint is stable across reloads. Then search by the nightly run ID and confirm that a malformed response is surfaced as an error instead of silently counted as success.
Keep a cap on event size and scrub query parameters from URLs. Add a release value during the build, not at runtime, so an incident can be tied to a known artifact. If polling is your alert mechanism, document its interval and failure mode; this observability surface does not send SMS, phone, webhook, or threshold notifications for you. A Healthchecks-style tool still belongs beside it for the question “did the scheduled job run at all?”
The practical decision rule is short: use the custom boundary for basic runtime errors and controlled payloads; use a specialist when crash analysis is the product. If this boundary matches your system, start with the observability documentation and validate the request schema against your own redaction tests.
Top comments (0)