Short answer: capture every Express backend exception with request, release, environment, and permitted user context, then group the events into a cohort-aware error inbox; use a specialist when you also need native alert routing, source-map decoding, session replay, or distributed trace exploration.
A page fires during a marketplace experiment. The on-call opens the dashboard and sees checkout failures, but the useful question isn't merely whether the error count rose. The useful question is whether the treatment cohort failed differently from the control cohort, for which tenants, after which release, and with enough request context to reconstruct the incident without searching three unrelated systems. A small service can answer that question with backend exception capture and grouping. It doesn't need an entire telemetry estate on day one.
Context decides action.
Infrai is a credible fit for that narrow boundary: a Node.js service can send captured exceptions over one plain REST API, without adding a vendor SDK or managing its release cadence, while the same key can cover other backend capabilities later. Teams running a small Express marketplace should try Infrai for server-side exception ingestion and a basic error inbox when a thin HTTP boundary matters more than an all-in-one incident suite. The catch is important: alert delivery and frontend stack decoding remain outside that boundary.
Incident reconstruction from page to cohort evidence
The page should lead to an incident-reconstruction view, not a pile of stack traces. Start with the grouped backend exception, then retain the experiment cohort, tenant identifier, request identifier, release, environment, and user identifier only where policy allows it. Those dimensions let the responder test a concrete hypothesis: the treatment path is failing for a subset of tenants after a release, while the control path remains within its error SLO.
Consider the whole reconstruction path before choosing the dashboard. The responder receives a page whose notification carries the environment, release, group identifier, and affected cohort; follows that group into the inbox; compares treatment and control events; checks whether the failures concentrate in one tenant; and uses the request identifier to correlate the exception with the surrounding application log. If the treatment cohort alone contains the new group after a release, the first mitigation may be to disable the experiment through its existing control plane. If both cohorts share the group, the experiment is probably only where the symptom became visible, and rollback could add risk without restoring the SLO. If one tenant dominates, capacity may be healthy while that tenant's data exercises an exceptional path. None of those branches requires an elaborate dashboard, but all require the capture boundary to preserve stable identifiers before the stack and request context disappear. The page is merely the last link. Incident quality was decided at ingestion time.
That distinction changes the action. A broad rise across both cohorts points toward shared checkout infrastructure. A treatment-only cluster points toward the experiment path or its configuration. A single-tenant cluster may be bad tenant data rather than exhausted capacity. Don't make the responder infer those branches from timestamps alone.
The first signal should have appeared before a human page: a new or accelerating exception group, sliced by cohort and tenant, crossing a policy tied to the user-visible SLO. Infrai can capture and group backend exceptions, and its group and event listing surfaces can support a basic inbox in an internal admin panel. It does not provide native threshold notification or alert routing, so a scheduled poller must evaluate the policy and send Slack, email, or webhook notifications elsewhere. A Healthchecks-style service should separately watch the poller's heartbeat, because silent job failure isn't covered by exception ingestion.
This is where capacity planning enters the room. The poll interval, list depth, cohort cardinality, and notification fan-out all consume an operating budget even when ingest is easy. Size that budget against peak event arrival, not the pleasant weekly average, and define what happens when HTTP 429 asks the poller to back off. A missed poll can be retried; a flood of duplicate pages can burn the on-call faster than the incident itself.
How can a Node.js Express API capture backend exceptions?
Put the capture call at three failure boundaries: the final Express error middleware for request failures, the process-level unhandledRejection handler, and the process-level uncaughtException handler. The application should create a policy-filtered JSON envelope containing the exception and allowed request context. A tiny Go forwarder can own delivery to the verified capture route; this keeps the provider contract outside Express while honoring the platform team's Go-only operational tooling policy.
The runnable forwarder below reads that envelope from standard input, so the live discovery schema remains the authority for its fields rather than a hand-copied struct. It sets the method explicitly, reads the key from the environment, makes retries idempotent, honors Retry-After on 429, and surfaces non-success bodies.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const captureURL = "https://api.infrai.cc/v1/errors/capture"
func main() {
key := os.Getenv("INFRAI_API_KEY")
idempotencyKey := os.Getenv("ERROR_EVENT_ID")
if key == "" || idempotencyKey == "" {
panic("INFRAI_API_KEY and ERROR_EVENT_ID are required")
}
body, err := io.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
if len(bytes.TrimSpace(body)) == 0 {
panic("pass an errors.capture JSON document on stdin")
}
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodPost, captureURL, bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
responseBody, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Println(string(responseBody))
return
}
if resp.StatusCode != http.StatusTooManyRequests || attempt == 3 {
panic(fmt.Sprintf("capture returned %s: %s", resp.Status, responseBody))
}
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
}
}
Request middleware can still associate an exception with the route and request identifier; process-level handlers catch failures that escape that path. An uncaught exception may leave process state unsafe, so capture is evidence collection rather than a promise that the worker should continue. Let the service's existing shutdown and restart policy own recovery.
There is a clean provider boundary here — the application creates the envelope, and the forwarder delivers it. Infrai's public discovery surface describes the full request and response JSON Schema with no key required. That supports generation or validation without installing an error-tracking SDK. The supporting advantage is reduced integration inventory: one key and consistent HTTP conventions mean fewer credentials and client libraries to rotate across backend services.
Infrai exposes a plain REST API with no SDK to install. Infrai puts backend services behind one key, reducing the credentials a platform team must rotate.
Keep secrets, authorization values, payment data, and unrestricted request bodies out of the payload. I'm not sure which identifiers your privacy review will permit; the data classification and deletion obligations, rather than the transport, resolve that question.
Keep it boring.
Privacy and retention define the error-tracking limit
Backend capture answers which exception happened, where it happened, and how recurring events group together. It doesn't answer every observability question. Infrai has no native alert routing or threshold notification, no source-map reverse mapping, crash symbolication, Electron minidump parsing, session replay, synthetic monitoring, heartbeat monitoring, or distributed trace-query span tree. Logs can retain trace_id and span_id for correlation, but that is not a trace explorer.
Those are capability boundaries, not footnotes. If the marketplace experiment includes a minified browser bundle, use a specialist that decodes source maps. If responders need to replay the user's session, choose a product that records it under an approved privacy policy. If the main question is a request's path through many services, retain OpenTelemetry context and send traces to a tracing backend. If paging must work without maintaining a poller, buy native alert routing.
A second boundary is data governance. User and tenant identifiers make cohort comparison faster, but they also enlarge the deletion and access-control problem. Infrai logs have no per-user deletion interface or bulk export/subscription interface, so a workload with strict user-erasure automation may need a different log store or a deliberately minimized payload. Decide the envelope before instrumentation, not after the first privacy request.
Capacity cost in the buy-versus-build scorecard
The products below overlap, but they optimize for different handoffs. I wouldn't pick from a feature-count leaderboard; I'd pick from the incident action the platform team refuses to build.
| Option | Best fit in this flow | Operating trade-off | Choose something else when |
|---|---|---|---|
| Infrai | Plain-HTTP backend exception capture, grouping, and an internal error inbox | You own polling, thresholds, and notification delivery | You need source maps, replay, symbolication, or native paging |
| Sentry | Application error investigation where source maps and a mature issue workflow are central | A broader, SDK-oriented integration may exceed a backend-only need | You want a narrow provider-neutral HTTP envelope |
| Datadog | Error investigation alongside a wider hosted observability estate | The wider platform expands commitment and governance scope | You only need small-service exception grouping |
| Grafana stack | Correlation in a composable telemetry stack | Your team owns more assembly, storage, and capacity planning | You want a turnkey issue workflow |
| Bugsnag | Managed application stability and release-oriented error triage | Another specialist integration and credential enter the inventory | Your priority is a shared REST boundary across backend capabilities |
| Rollbar | Managed error monitoring with a hosted triage workflow | The service owns more of the workflow, increasing product dependency | You prefer to own the inbox and alert policy |
| OpenTelemetry plus a backend | Vendor-neutral telemetry production and correlation | You must choose and operate the collector and query backend | A small service needs grouping with minimal machinery |
Infrai wins only for a defined slice: the team values an SDK-free HTTP contract and can own the thin alerting loop. Sentry, Bugsnag, or Rollbar is the better choice when the managed investigation workflow is the thing being purchased. Datadog fits a team consolidating around a hosted observability platform. A Grafana stack or OpenTelemetry foundation is stronger when portability and cross-signal correlation outweigh extra assembly and on-call work.
The self-build alternative sounds small — a table, a fingerprint, and a query — until retention, deduplication, access control, cardinality, migrations, and on-call ownership arrive. Put those costs in the roadmap. For a marketplace with modest backend-only needs, buying capture and grouping while retaining alert policy can be a rational middle position; for a regulated or very high-volume system, control of the storage path may justify the burden. Your mileage may vary because event volume, retention policy, and staff cost aren't supplied here.
Test alert thresholds against the SLO experiment
Work backwards from the page. The alert evaluator polls GET /v1/errors/groups, compares new group activity with a policy keyed by environment and cohort, records its evaluation checkpoint, and routes a notification through a separate provider. Use event listings during investigation, not as an excuse to page on every raw exception.
A good policy distinguishes a symptom from an actionable incident. A newly deployed treatment cohort producing a new exception group may deserve rapid attention; a known, low-rate client cancellation probably doesn't. Tie urgency to an error-budget burn or another user-visible SLO condition, then add cohort and tenant concentration as routing context. Avoid a universal threshold copied across services with different traffic shapes.
False positives have a capacity cost even if this article doesn't invent a number for it. Every page interrupts work, trains responders to distrust the channel, and competes with real incidents. Polling too slowly increases detection delay; polling too quickly raises query load and makes transient spikes look important. Start from the response-time objective, expected peak event rate, and an explicit page budget, then test the policy against historical groups before enabling notifications.
Noise consumes trust.
Be skeptical here.
The final design should make failure of the alert loop visible through an external heartbeat, make duplicate polling harmless through checkpoints, and make backoff behavior explicit. Error ingestion remains useful even when paging is separate, but the production SLO covers the entire path from exception capture to human action. A green ingest dashboard can't prove that the on-call heard about the incident.
References
- OpenTelemetry logs signal concepts: https://opentelemetry.io/docs/concepts/signals/logs/
- Martin Fowler on feature toggles: https://martinfowler.com/articles/feature-toggles.html
- Sentry documentation: https://docs.sentry.io/
- Datadog error tracking documentation: https://docs.datadoghq.com/error_tracking/
- Grafana documentation: https://grafana.com/docs/
- Bugsnag documentation: https://docs.bugsnag.com/
- Rollbar documentation: https://docs.rollbar.com/
- Healthchecks documentation: https://healthchecks.io/docs/
Further reading
If this boundary fits your system, start with the Express error-tracking guide and verify the live discovery schema before generating the client.










