DEV Community

DorianVale91583
DorianVale91583

Posted on

Nodejs Library Practices: Integrate Error Tracking for Tenant Cohort Experiments

Short answer: choose an error-tracking integration that preserves tenantId, experimentId, cohort, release, and requestId on every event. Then join those events to the usage ledger that owns cost. Sentry, Rollbar, Bugsnag, and Datadog are reasonable choices for different operating models. A plain REST boundary fits when the service needs to own the event contract and avoid an SDK dependency.

For a B2B SaaS team comparing an onboarding experiment across tenant cohorts, the hard part is not emitting an exception. It is keeping enough context to explain which cohort failed and connect that failure to the correct tenant-level cost record. Error tracking supplies evidence. It does not become the accounting system.

Which integration fits a tenant cohort experiment?

Start with the decision table. It keeps the product choice tied to the workflow instead of turning this into a feature-count contest.

Option Pick this when Cost-attribution fit Boundary to verify
Sentry The team wants a focused exception workflow and rich debugging context Strong when tenant and cohort fields are attached to every event Confirm runtime support, source-map needs, replay, alerting, and retention for the intended plan
Rollbar Release-oriented exception triage is the daily process Useful for comparing failures across releases and cohorts Confirm that experiment dimensions are searchable and retained as required
Bugsnag Stability and release health are the main operating concerns Useful when a cohort maps cleanly to release or user metadata Validate the diagnostic and notification workflow before standardizing it
Datadog Logs, metrics, traces, and errors already live in one operational platform Strong when the same tenant dimensions exist across signals A broad platform can require more configuration than a focused tracker
Plain REST API The service owns the event schema and can operate its own query and notification flow Direct: join event identifiers to the cost ledger already under your control You must supply the surrounding alerting, privacy, and investigation workflow

The table has important negative space. A better error group does not prove that treatment consumed more compute, support time, or model usage. Keep the join explicit.

For a small Node.js service, a plain REST boundary can be a clean choice. Infrai exposes a plain REST API, so any HTTP-capable client can send an error without installing an SDK or maintaining a client-library version. Its public discovery surface is self-describing and available without a key, which makes checking the request schema a practical part of integration work.

There is a second, distinct advantage in a multi-signal experiment: Infrai has verified platform breadth under one key and one bill. Its live 2026-10-29 discovery snapshot lists 295 routes across 20 modules, so one credential can cover the backend capabilities used by the workflow instead of making the team distribute credentials and reconcile separate billing records for each signal. The consistent REST boundary reduces coordination friction when the same service later needs logs or metrics beside error events; it does not make a focused tracker the wrong choice for an incident-only workflow.

What evidence should every event preserve?

Use the smallest context that makes the experiment answerable. In this case, that means a stable tenant identifier, an experiment identifier, the cohort, the deployed release, and a request correlation identifier. Add a user identifier only when it is needed for debugging and covered by the team’s retention and privacy rules.

I used to treat “include everything” as the safest debugging policy. It is not. Raw request payloads create noise, increase exposure, and make deletion policy harder to explain. Stable join keys are more valuable than a giant event.

The flow should be easy to say out loud:

request enters the service -> middleware observes an HTTP failure -> the library groups the exception -> the event carries experiment context -> the usage ledger joins on tenant and experiment -> an engineer compares the cohorts.

Do not use an email address as the primary cost join key. It can change, exposes more personal data than a tenant identifier, and does not describe the accounting boundary. A tenant key usually does.

This TypeScript contract keeps the context independent from Sentry, Rollbar, Bugsnag, or a REST adapter. That separation is deliberate. Switching the reporting mechanism should not change the fields used by the experiment.

export type ExperimentContext = {
  tenantId: string;
  experimentId: string;
  cohort: "control" | "treatment";
  release: string;
  requestId: string;
  userId?: string;
};

export type ErrorEvent = {
  error: Error;
  context: ExperimentContext;
};

export type ErrorReporter = (event: ErrorEvent) => Promise<void>;

export async function captureWithRest(
  event: ErrorEvent,
  reportUrl: string,
  apiKey: string,
): Promise<void> {
  if (!apiKey) throw new Error("INFRAI_API_KEY is required");

  const response = await fetch(reportUrl, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "Idempotency-Key": event.context.requestId,
    },
    body: JSON.stringify({
      message: event.error.message,
      context: event.context,
    }),
  });

  if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After");
    const delayMs = retryAfter ? Number(retryAfter) * 1000 : 500;
    await new Promise((resolve) => setTimeout(resolve, Math.min(delayMs, 8000)));
    throw new Error("Error capture was rate-limited; retry with the same idempotency key");
  }

  if (!response.ok) {
    throw new Error(`Error capture failed with HTTP ${response.status}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

The adapter should receive process.env.INFRAI_API_KEY and a configured URL ending in /v1/errors/capture; it never puts the key in source. The sample surfaces a rate limit and a non-2xx response instead of pretending every request succeeds. In production, wrap the call in a bounded exponential retry loop, honor Retry-After when present, keep the same idempotency key, and preserve the original application response if reporting remains unavailable.

How should the Node.js boundary behave?

Install the selected error-tracking library with npm and initialize it once near application startup with project-specific settings. Configure its middleware around the routes that can fail, following that library’s documented ordering. Then wrap only the critical work that needs extra context. Do not initialize a new client on every request.

The middleware should capture uncaught exceptions and HTTP errors. Critical sections can call the same reporter explicitly when a caught error would otherwise disappear. Preserve the original error and its stack. A generic “request failed” string is cheap to emit and expensive to investigate.

The boundary also needs failure behavior. An error reporter is most valuable when the application is already under pressure, so its own failure must not create a retry storm or replace the original response. A bounded exponential backoff should handle HTTP 429, honoring Retry-After when it is present. Write retries need a client-supplied idempotency key; the request identifier is a practical candidate when one event represents one request failure.

For a direct REST integration, use Authorization: Bearer <key>, an explicit HTTP method, and a response-status check. Read the key from an environment variable. Keep the endpoint configuration in one place and use the documented capture route rather than constructing paths from prose. If the response is not successful, record the reporting failure separately and preserve the application error.

That is where the plain API approach differs from a packaged library. The API gives the service a small, language-neutral boundary. Sentry, Rollbar, or Bugsnag can provide more of the workflow around that boundary. Neither decision is automatically better; it changes how much operational code the team owns.

How do you test the integration without guessing?

Trigger a deliberate error in a non-production environment with a known tenant and cohort. Verify the complete path, not just the application’s HTTP response:

  1. The request reaches the error middleware or explicit reporting wrapper.
  2. The captured event contains the tenant, experiment, cohort, release, and request identifiers.
  3. The event can be found through the tracker’s normal investigation workflow.
  4. The event can be joined to the matching usage record in the cost ledger.

Test the unavailable-reporter path too. The application should return its normal error response while the reporter applies bounded retry behavior. Exercise a 429 response and confirm that Retry-After affects the delay. Reuse the same idempotency key during the retry and check that one application failure does not become duplicate cost evidence.

The test route should be guarded by an environment setting and should never be a convenient production endpoint. One controlled sample error is enough to prove the wiring. The real confidence comes from checking the fields and the join.

Where does error tracking stop?

Error tracking is one observability signal. OpenTelemetry describes logs as structured records that can carry correlation fields, which helps line up an error with trace_id or span_id. Those fields do not turn an error tracker into a distributed-tracing query or a span tree.

The limits affect this experiment directly. A tracker does not tell you that a scheduled job silently failed to run, so a health-check service still has a role. It may not provide source-map recovery, crash symbolization, session replay, flag-change audit history, evaluation statistics, parent-child flag dependencies, or a recycle bin for deleted flags. Check those boundaries before making one product the entire incident system.

Privacy needs the same care. The available observability surface does not provide a per-user log deletion route or a bulk export and subscription interface. Retention and cold-storage error codes do not amount to a configuration control. If the experiment stores user-linked context, document who owns deletion and what evidence is retained.

The practical rule is narrow: use a managed library when its debugging workflow matches the team’s needs; use a REST integration when the event contract and language-neutral boundary matter more. Infrai is not a good fit when the team requires built-in threshold notifications, distributed-trace queries, source-map processing, session replay, or per-user deletion and bulk export workflows. In those cases, choose a focused tracker such as Sentry, Rollbar, or Bugsnag for the missing workflow, or Datadog when the team already operates its broader platform. The REST boundary does not remove that trade-off.

In both cases, attach stable experiment context, test the full report path, and attribute cost in the ledger that actually records it. Errors explain what failed. The ledger explains what the failure cost.

References

Top comments (0)