# Sentry Node.js: preload instrument.js before everything else

## The pattern

Create `instrument.js` (or `.mjs` for ESM) at the project root:

```js
const Sentry = require("@sentry/node");

Sentry.init({
  dsn: "___PUBLIC_DSN___",
  tracesSampleRate: 0.1, // docs show 1.0; lower it for production
});
```

Then preload it so it runs before your app code:

```
node --import ./instrument.js app.js
```

For ESM use `instrument.mjs` and `node --import ./instrument.mjs app.mjs`. The whole point is ordering: anything imported before `Sentry.init()` runs misses auto-instrumentation (http, db, etc.). If you use a bundler like Vite, follow the bundler setup docs instead of the preload flag.

## Version floor

Current docs require Node >=20.19.0 (<22), >=22.12.0 (<23), or >=23.2.0. Older Node versions silently lose instrumentation or fail to load. If your Dockerfile pins node:18 or node:20-slim from last year, this is the first thing to check when events stop.

## v11 changes that bite

- Span streaming is the default: spans ship in batches as they finish instead of one transaction at the end. `beforeSendTransaction` and `ignoreTransactions` no longer do anything.
- `sendDefaultPii` is gone, replaced by `dataCollection` with per-category control, and the v11 default collects MORE than the v10 default did. If you relied on v10's restrictive default, set the categories explicitly.
- `setTag`/`setExtra` no longer land on spans; use `setAttribute`/`setAttributes` for anything that must be searchable on spans.

## Verify

Drop a caught exception behind a setTimeout (like the docs verify snippet), run with the preload flag, and confirm the issue appears. If not, check the DSN and that no adblocker or egress proxy sits between the host and the ingest endpoint.