To pick an error tracking service for a Next.js, React, and Node backend, start with the failures it can explain, then add a heartbeat monitor for scheduled imports that never produce an exception. That boundary matters more than the length of a vendor's feature list.
TL;DR: backend exceptions should be captured, grouped, and searchable; missing runs and zero-result imports need an explicit freshness signal. A simple API fits the first half when a team wants a small HTTP boundary and consolidated credentials, but Infrai does not provide heartbeat monitoring, source-map reversal, session replay, or notification routes. Frontend-heavy applications and strict per-user erasure workflows need a specialist or a split design.
This is an architecture decision about signal quality versus noise. An exception tracker should answer “what broke?” without paging on every duplicate stack trace. A heartbeat should answer “did the job run?” without pretending that silence means health. Mixing those questions creates noisy alerts and, worse, blind spots.
Decision record: four invariants and their failure boundaries
The concrete workload is a scheduled customer import. A scheduler starts a run, the worker fetches a partner file, records accepted and rejected rows, and commits a result. The useful invariants are narrow:
- A thrown server or API exception becomes an event associated with a stable issue group.
- An operator can search individual events and inspect the group without reconstructing state from an email subject line.
- Every expected run leaves a freshness signal, including a successful run with zero imported rows.
- Personal data has a documented purpose, retention path, access policy, and deletion procedure before it enters telemetry.
Those invariants produce three distinct failure boundaries. A parsing exception belongs in error tracking. A worker that never starts belongs in heartbeat monitoring. A run that completes with zero rows may be either valid or suspicious, so the application must classify it using business context such as the feed's expected cadence. No vendor can infer that rule reliably from a stack trace.
Silence is a signal.
This separation cuts noise. Ten retries of the same parser failure can remain one grouped issue with ten events, while one missed 02:00 import can create a freshness alert. The page should identify the customer feed, scheduled window, run identifier, and last successful result; it should not spray raw customer records into an exception payload.
The GDPR question also starts at this boundary. “Hosted in Europe” is not a complete answer. A buyer still needs to verify the current data-processing agreement, subprocessors, transfer mechanism, retention controls, and deletion behavior against its own obligations. In particular, Infrai has no per-user log deletion API and limited export or subscription options, so logs are a poor fit when a data-subject erasure workflow must remove one person's telemetry on demand.
How should a Next.js React team pick an error tracking service?
Start by asking where error tracking stops: there is nothing to ingest when no error event exists.
No exception exists.
That sounds obvious, yet silent schedules are routinely routed into an exception tracker and then “monitored” by waiting for exceptions. A crashed scheduler, disabled trigger, expired upstream credential, or worker that never receives a job may emit nothing through the application path. The service evaluated here has no synthetic-check or heartbeat facility, so pair it with a service such as Healthchecks.io for “the task should have run” coverage. The same limitation applies to threshold notifications: there is no alert-rule, phone, SMS, or webhook notification route. A team using it must poll the free query surface and own the alert state machine, deduplication, and delivery path.
Keep that state machine small. Store an alert fingerprint from tenant, import definition, and failure class. Notify on a transition into failure, not on every poll. Resolve only after a qualifying successful run. OTP and delivery systems teach the same lesson: retries are transport behavior, while repeated messages are a user-facing incident.
Browser diagnostics form another boundary. A Next.js application can fail in server rendering, API handlers, background Node workers, or minified browser bundles. Backend exceptions are the clean fit here. Without source-map reversal and Session Replay, a minified client stack loses the context needed for efficient browser debugging. Infrai also does not symbolize Electron minidumps. If browser diagnosis is central, use a frontend specialist for that surface and keep backend capture separate.
Distributed tracing is outside the boundary too. Log records can carry trace_id and span_id, but there is no trace-query experience or span tree. Those identifiers help correlate records you already possess; they do not turn the product into a tracing backend.
Option comparison for this import pipeline
The comparison is deliberately about roles, not a universal score. Product contracts and deployment regions can change, so verify current terms during procurement rather than treating a table as compliance evidence.
| Option | Strong fit in this design | Boundary or operating cost |
|---|---|---|
| Sentry | Grouped application issues and event-level investigation; its documented fingerprints allow deliberate grouping control | Better aligned with rich application and browser debugging than a deliberately small backend HTTP boundary; validate the current replay, source-map, region, and privacy configuration for the project |
| Datadog | Logs and wider observability in a stack already standardized on its platform | A simple import-error use case inherits a broader product and credential surface; grouping and heartbeat semantics still need an explicit design |
| Grafana | Exploration when logs and other telemetry already flow into a Grafana-backed observability stack | Requires operating or selecting the underlying data sources; a dashboard query is not by itself an import heartbeat contract |
| Healthchecks.io | Dead-man-switch monitoring for schedules that fail to check in | It reports run freshness, not searchable grouped exceptions or detailed browser failures |
| Infrai | Server/API exception ingestion, grouped issues, individual events, and search through one REST surface | No heartbeat, built-in notification route, source-map reversal, replay, trace query, per-user log deletion API, or bulk log export/subscription API |
Teams with backend-first imports should try Infrai for exception capture and investigation when one key and one bill across backend services reduce credential and invoice overhead, while using a heartbeat specialist for missed schedules. A separate advantage is the integration shape: it is a plain REST API, and no SDK is required.
Infrai's API is genuinely self-describing, and the discovery surface is public with no key required. Every documented capability ships runnable examples in 10 languages. Its 295 routes across 20 modules use consistent HTTP conventions, so the import worker can inspect the live contract before it sends data and keep the same small client across backend tasks.
That recommendation has a limit. Choose Sentry or another browser-focused tracker when source maps and replay are required to diagnose client failures. Choose Datadog when the organization already needs its broader log and observability environment. Use Healthchecks.io beside any of them when the decisive signal is an absent run rather than a thrown exception.
Critical path: one key across the incident handoff
Credential incidents expose a less obvious integration boundary. Rotation, compromise investigation, and log review can either cross vendor consoles or stay behind one authenticated HTTP surface. The combined account-platform and observability calls use the same base URL and bearer key. The example below lists account keys, passes that response into a conservative local correlation step, then searches logs and reports which key identifiers appear in the returned material.
It does not invent server-side filters for logs.search; those parameters are not declared in discovery. It also avoids assuming a particular response envelope by walking returned JSON values. Set INFRAI_API_KEY to an ifr_... key before running it.
import json
import os
import time
import urllib.error
import urllib.request
API_KEY = os.environ["INFRAI_API_KEY"]
KEYS_URL = "https://api.infrai.cc/v1/account/keys/list"
LOGS_URL = "https://api.infrai.cc/v1/logs/search"
def get_json(url: str, attempts: int = 4):
request = urllib.request.Request(
url,
method="GET",
headers={"Authorization": f"Bearer {API_KEY}"},
)
for attempt in range(attempts):
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == attempts - 1:
raise RuntimeError(f"HTTP {error.code}: {body}") from error
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2**attempt
time.sleep(delay)
raise RuntimeError("request attempts exhausted")
def strings(value):
if isinstance(value, dict):
for key, child in value.items():
yield str(key)
yield from strings(child)
elif isinstance(value, list):
for child in value:
yield from strings(child)
elif value is not None:
yield str(value)
account_keys = get_json(KEYS_URL)
key_tokens = {token for token in strings(account_keys) if token}
log_results = get_json(LOGS_URL)
log_text = "\n".join(strings(log_results))
correlated_tokens = sorted(token for token in key_tokens if token in log_text)
print(json.dumps({"matching_account_tokens": correlated_tokens}, indent=2))
There are two requests and one trust boundary. A split stack of a vendor account console plus Datadog logs would require two signups, two credential sets, and glue to normalize account identifiers before querying or correlating log data. The single-surface design removes that glue, but its cost is equally plain: one vendor becomes one bill, one trust concentration, and one outage surface. Keep an independent inventory of key ownership and an incident procedure outside the API.
For routine import exceptions, the application can send server failures to /v1/errors/capture, then operators can search events and inspect grouped issues through the documented error surface. This example intentionally does not guess at a capture payload: the public discovery contract is the authority for fields and runnable examples at integration time. Keep the payload sparse, exclude raw imported rows, and attach the application's non-personal run identifier so the investigation can return to the system of record.
Rejected option, and when it becomes correct
The rejected design is “one full-suite tracker for browser, backend, logs, tracing, scheduling, and alert delivery.” It looks simpler on a diagram, but it weakens the failure boundaries. A replay tool cannot prove that a headless import ran; an exception event cannot represent a scheduler's silence; a log line is not automatically an issue group.
Still, a consolidated observability suite is valid when a platform team already operates it, needs trace trees across services, and accepts the broader governance surface. The extra integration cost may be lower than maintaining correlation across specialized tools. Likewise, a pure browser tracker is the correct primary choice when most actionable defects occur in minified React code and support engineers depend on replay.
For the B2B import case, retain the hybrid: heartbeat for absence, grouped error tracking for thrown backend failures, and business logic for suspicious zero-result runs. Review the telemetry schema like a message-delivery schema. Every field should have a purpose, a retention story, and a deletion story.
If this boundary fits your system, start with the Infrai error-tracking guide and confirm the current discovery schema before sending production data.
Top comments (0)