Skip to content

Node.js (Sentry-compatible SDK)

Use the official Sentry SDK for Node.js with a Belvador DSN and the data-scrubbing hook Belvador provides.

Markdown
On this page

For Node.js servers Belvador uses the official Sentry SDK for Node.js with your Belvador DSN. Belvador adds the init options below and its own scrubbing from @belvador/core/sentry — the code Belvador's SDKs run — which strips request bodies, cookies and credentials before anything leaves your server.

Before you start#

  • A project with platform Node.js and its DSN.

  • A Node.js version supported by the current @sentry/node release (see Sentry's Node.js guide).

1. Install#

Terminal
npm install @sentry/node@11.9.0 @belvador/core@0.6.1

@belvador/core is installed only for its stable @belvador/core/sentry entry: Belvador's scrubbing and captureResult for an official Sentry SDK. It loads no Sentry code of its own.

2. Create instrument.mjs#

instrument.mjs
import * as Sentry from "@sentry/node";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";Sentry.init({  dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>",  release: process.env.APP_VERSION,  environment: process.env.NODE_ENV,  sendDefaultPii: false,  tracesSampleRate: 0,  maxBreadcrumbs: 50,  maxValueLength: 8192,  // Belvador's scrubbing: request bodies, cookies and credentials never leave the app.  beforeSend: scrubEvent,  beforeBreadcrumb: scrubBreadcrumb,});

What the options do:

OptionWhy
dsnReads BELVADOR_DSN from the environment, with the project DSN as fallback
release, environmentYour app's version and environment, from APP_VERSION and NODE_ENV
sendDefaultPii: falseNo IP addresses, cookies or user details added by the SDK
tracesSampleRate: 0The Sentry SDK sends no spans; for traces use OpenTelemetry
maxBreadcrumbs: 50, maxValueLength: 8192Same bounds as Belvador's own SDKs
beforeSend: scrubEventRemoves request bodies, cookies, auth headers and client IP headers; masks values under sensitive names and credentials in text
beforeBreadcrumb: scrubBreadcrumbThe same scrubbing for breadcrumbs (URLs with tokens, logged values)

3. Load it before your app#

Terminal
node --import ./instrument.mjs server.js

The --import flag loads the instrumentation first in an ES module app. In a CommonJS app, require("./instrument") on the very first line of your entry file instead (rename the file to instrument.js and use require there).

Web frameworks#

@sentry/node instruments common frameworks. Express, Fastify, Koa and others need one extra line to report errors from their error handler — follow the framework section of Sentry's Node.js guide and keep the init options above. Belvador has not verified each framework.

Report errors you handle#

JavaScript
import * as Sentry from "@sentry/node";try {  await chargeCard(order);} catch (error) {  Sentry.captureException(error);  throw new PaymentFailed(order.id);}

Database errors that do not throw#

Supabase and PostgREST return { data, error } instead of throwing. Bind captureResult to your Sentry SDK once:

db.mjs
// captureResult on the app's own Sentry SDK (@sentry/node, @sentry/bun or @sentry/deno).import * as Sentry from "@sentry/node";import { captureResultWith } from "@belvador/core/sentry";export const captureResult = captureResultWith(Sentry);

Then wrap each call; the result comes back unchanged and the error is reported typed by its Postgres code. The types and options: Database errors.

On a VPS: keep errors across crashes#

Without help, @sentry/node keeps unsent errors in memory: a crash, a restart or a deploy while your node is unreachable loses them. Add @belvador/spool and use this instrument.mjs instead:

instrument.mjs
import * as Sentry from "@sentry/node";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";import { withSpool } from "@belvador/spool";Sentry.init({  dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>",  release: process.env.APP_VERSION,  environment: process.env.NODE_ENV,  sendDefaultPii: false,  tracesSampleRate: 0,  maxBreadcrumbs: 50,  maxValueLength: 8192,  // Belvador's scrubbing: request bodies, cookies and credentials never leave the app.  beforeSend: scrubEvent,  beforeBreadcrumb: scrubBreadcrumb,  // Errors that cannot be sent wait on disk and go out after a restart.  transport: withSpool(Sentry.makeNodeTransport, {    dir: process.env.BELVADOR_SPOOL_DIR ?? "/var/lib/my-app/belvador",  }),});
  • Point BELVADOR_SPOOL_DIR at a directory that survives restarts and that only your app's user can read — with systemd, StateDirectory=my-app gives you /var/lib/my-app.

  • Errors wait there (at most 64 MiB, 72 hours) and are sent by the next process once your node answers. Tested: errors made while the node was unreachable arrived after the process was killed with SIGKILL, and after it died of out-of-memory.

  • An out-of-memory crash itself is not reported — the process dies before any code can run — but every error it had already kept is.

Details: Offline and durable delivery.

Set it up with an AI agent#

Paste this into Claude Code, Cursor or another coding agent. It contains the files above for a server that keeps errors across crashes, and the rules it must follow:

Text
Belvador errors: Node.js (@sentry/node)Tested with: @sentry/* 11.9.0Guide: https://belvador.dev/en/docs/sdk/nodeGoalReport this server's errors to Belvador with the official Sentry SDK and Belvador's scrubbing, exactly as in the files below. Keep any other error SDK out: one Sentry.init per process.RulesCopy the files as they are and keep every Sentry.init option: sendDefaultPii: false, tracesSampleRate: 0, maxBreadcrumbs: 50, beforeSend: scrubEvent, beforeBreadcrumb: scrubBreadcrumb. Read the DSN from BELVADOR_DSN; do not invent other environment variables. Do not change authentication, routing or business logic. Report errors the code already handles with captureException (report() on Supabase Edge Functions) and wrap Supabase or PostgREST results that return an error instead of throwing with captureResult, without changing what the code does with the result.```bashnpm install @sentry/node@11.9.0 @belvador/core@0.6.1``````bashnpm install @belvador/spool@0.6.1```instrument.mjs```javascriptimport * as Sentry from "@sentry/node";import { scrubBreadcrumb, scrubEvent } from "@belvador/core/sentry";import { withSpool } from "@belvador/spool";Sentry.init({  dsn: process.env.BELVADOR_DSN ?? "https://<public-key>@ingest.belvador.dev/<project-id>",  release: process.env.APP_VERSION,  environment: process.env.NODE_ENV,  sendDefaultPii: false,  tracesSampleRate: 0,  maxBreadcrumbs: 50,  maxValueLength: 8192,  // Belvador's scrubbing: request bodies, cookies and credentials never leave the app.  beforeSend: scrubEvent,  beforeBreadcrumb: scrubBreadcrumb,  // Errors that cannot be sent wait on disk and go out after a restart.  transport: withSpool(Sentry.makeNodeTransport, {    dir: process.env.BELVADOR_SPOOL_DIR ?? "/var/lib/my-app/belvador",  }),});```db.mjs```javascript// captureResult on the app's own Sentry SDK (@sentry/node, @sentry/bun or @sentry/deno).import * as Sentry from "@sentry/node";import { captureResultWith } from "@belvador/core/sentry";export const captureResult = captureResultWith(Sentry);```VerifyStart the app locally with BELVADOR_DSN set and trigger one test error (a route or a one-off script that throws). Confirm it appears as an issue in the Belvador project. Do not send test errors to production unless the user says so.ReportList the files you changed, the packages added with exact versions, the environment variables the app now needs, the test error you saw arrive, and anything you could not verify.

Check that it works#

Run a one-off script with the same instrumentation:

JavaScript
import * as Sentry from "@sentry/node";Sentry.captureException(new Error("Belvador Node.js test"));await Sentry.flush(2000);

The issue appears in the project within a few seconds.

Limits to know#

  • Belvador's scrubbing is pattern-based. A secret under an unusual name, or inside a free-text message, can slip through.

  • Your node stores error events, attachments, check-ins and Sentry logs, and counts delivery reports. Keep tracesSampleRate: 0; tracing data and release-health sessions are counted as Not supported yet in the project's intake numbers and dropped. @sentry/node 11 sends request sessions and Sentry.logger calls by default, so expect a small Not supported yet count.

  • Scheduled jobs and workers can report runs with Sentry.withMonitor — see Monitors with the official Sentry SDKs.

  • Windows services: the same recipe applies, but Belvador has not run it on Windows yet.

Posts on this topic

All posts

Spotted something wrong or missing? Tell us: hello@belvador.dev