# JavaScript & TypeScript

> **For agents:** paste the DSN from `list_keys` into the snippet for the platform; create the project with the matching platform id (`create_project(org="acme", slug="web", name="Web", platform="nextjs")`). Platforms on this page: `nextjs`, `react`, `nuxt`, `sveltekit`, `angular`, `remix`, `tanstack`, `browser`, `node`, `hono`, `bun`, `adonis`, `nestjs`.

Every JavaScript platform uses the official `@sentry/*` package for that framework, pointed at a Bugwatch DSN. Browser bundles are minified, so **source maps matter more here than anywhere else** — see [Source maps](https://docs.bugwatch.io/sdks/source-maps.md) for the debug-ID and `sentry-cli` flow. Server and edge runtimes report unminified frames and rarely need maps.

Two conventions apply across the family:

- Set `release` in every init to the same string you upload maps under (git SHA or version); Bugwatch resolves frames by debug ID first, then by release + URL path.
- Browser SDKs post cross-origin to `ingest.bugwatch.io`; the ingest Worker answers CORS preflights and exposes `X-Sentry-Rate-Limits` / `Retry-After` so SDK back-off works.

Cloudflare Workers customers have a second option that keeps the request path SDK-free: deploy a tail Worker built with `createTailWorker()` from `@bugwatch/tail-worker` (set `BUGWATCH_DSN`) and add `"tail_consumers": [{"service": "<name>"}]` to the producer Worker — exceptions are captured from the tail stream and shipped as native envelopes.

Replace `https://<public_key>@ingest.bugwatch.io/<project_number>` with the DSN from `list_keys` or the project's settings page.

## Next.js

- Platform id: `nextjs` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/nextjs`
- Files: instrumentation-client.ts · sentry.server.config.ts · sentry.edge.config.ts

```ts
// instrumentation-client.ts (browser) — also sentry.server.config.ts and sentry.edge.config.ts
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>",
  tracesSampleRate: 1.0,
  release: process.env.NEXT_PUBLIC_RELEASE, // set from your CI (git sha or version)
  environment: process.env.NEXT_PUBLIC_VERCEL_ENV ?? "production",
});

// next.config.js
const { withSentryConfig } = require("@sentry/nextjs");
module.exports = withSentryConfig(nextConfig, {
  sentryUrl: "https://api.bugwatch.io",  // source maps upload here (sentry-cli compatible)
  org: "your-org", project: "your-project", authToken: process.env.BUGWATCH_TOKEN,
  widenClientFileUpload: true, hideSourceMaps: true,
});
```

Lifecycle notes:

- Three runtimes, three configs: the browser bundle (instrumentation-client.ts), the Node server (sentry.server.config.ts) and the Edge runtime (sentry.edge.config.ts) each init separately.
- Wrap App Router errors with app/global-error.tsx — React error boundaries don't catch root layout errors otherwise.
- Server Components and Route Handlers are auto-instrumented; middleware runs on Edge and reports through the edge config.
- withSentryConfig uploads source maps at build time through the chunk-upload API (sentry-cli compatible); set release in both configs so stack traces resolve.

Source maps: Uploaded automatically by withSentryConfig at `next build` — point `sentryUrl` at the Bugwatch API.

## React

- Platform id: `react` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/react`
- Files: src/main.tsx

```tsx
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>",
  integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
  tracesSampleRate: 1.0,
  release: import.meta.env.VITE_RELEASE,
});

// Catch render errors with a boundary (falls back to your UI, reports the tree)
<Sentry.ErrorBoundary fallback={<p>Something went wrong.</p>}>
  <App />
</Sentry.ErrorBoundary>
```

Lifecycle notes:

- Init before ReactDOM renders — errors during the first render are otherwise lost.
- Render errors only reach you through Sentry.ErrorBoundary (or a React 19 onCaughtError hook); event handlers and async code report via the global handlers.
- For React Router, use the router instrumentation so transactions are named by route pattern, not URL.

Source maps: Upload with `sentry-cli sourcemaps upload --url https://api.bugwatch.io ./dist` after the Vite build, with a matching `release`.

## Nuxt

- Platform id: `nuxt` · Language: TypeScript · Runs in: web
- Install: `npx nuxi module add @sentry/nuxt`
- Files: sentry.client.config.ts · sentry.server.config.ts

```ts
// sentry.client.config.ts
import * as Sentry from "@sentry/nuxt";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// sentry.server.config.ts (Nitro)
import * as Sentry from "@sentry/nuxt";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@sentry/nuxt/module"],
  sentry: { sourceMapsUploadOptions: { url: "https://api.bugwatch.io", org: "your-org", project: "your-project", authToken: process.env.BUGWATCH_TOKEN } },
});
```

Lifecycle notes:

- Client and Nitro server init separately; the module wires both plus Vue's errorHandler and Nitro's error hook.
- Server-side errors in server/api routes, plugins and middleware report through the Nitro side; hydration errors report from the client.
- Enable sourcemap: { client: true } in nuxt.config so client traces resolve after upload.

## SvelteKit

- Platform id: `sveltekit` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/sveltekit`
- Files: src/hooks.client.ts · src/hooks.server.ts

```ts
// src/hooks.client.ts
import * as Sentry from "@sentry/sveltekit";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });
export const handleError = Sentry.handleErrorWithSentry();

// src/hooks.server.ts
import * as Sentry from "@sentry/sveltekit";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });
export const handle = Sentry.sentryHandle();
export const handleError = Sentry.handleErrorWithSentry();
```

Lifecycle notes:

- Both hooks files must export handleError — SvelteKit swallows unexpected errors otherwise.
- Load functions are traced when wrapped with wrapLoadWithSentry (the Vite plugin does this automatically).

## Angular

- Platform id: `angular` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/angular`
- Files: src/main.ts · app.config.ts

```ts
import * as Sentry from "@sentry/angular";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// app.config.ts — Angular's ErrorHandler is the only path for template/zone errors
providers: [
  { provide: ErrorHandler, useValue: Sentry.createErrorHandler({ showDialog: false }) },
  { provide: Sentry.TraceService, deps: [Router] },
  provideAppInitializer(() => inject(Sentry.TraceService)),
]
```

Lifecycle notes:

- Errors inside the zone only reach you through the provided ErrorHandler — register it in the root providers.
- TraceService instruments router navigations as transactions; instantiate it eagerly with an app initializer.

## React Router (Remix)

- Platform id: `remix` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/react-router`
- Files: entry.client.tsx · entry.server.tsx

```tsx
// entry.client.tsx
import * as Sentry from "@sentry/react-router";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// entry.server.tsx
import * as Sentry from "@sentry/react-router";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });
export const handleError = Sentry.createSentryHandleError({ logErrors: true });
```

Lifecycle notes:

- Loaders and actions report through handleError on the server; the root ErrorBoundary must call captureException for client route errors.

## TanStack Start

- Platform id: `tanstack` · Language: TypeScript · Runs in: web
- Install: `npm install @sentry/tanstackstart-react`
- Files: src/client.tsx · src/server.ts

```tsx
// src/client.tsx
import * as Sentry from "@sentry/tanstackstart-react";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0, integrations: [Sentry.tanstackRouterBrowserTracingIntegration(router)] });

// src/server.ts (Nitro/Vinxi entry)
import * as Sentry from "@sentry/tanstackstart-react";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// Router: report route errors from the default error component
createRouter({ defaultErrorComponent: ({ error }) => { Sentry.captureException(error); return <ErrorView error={error} />; } });
```

Lifecycle notes:

- Server functions (createServerFn) are wrapped by the server init; route loaders report through the router's error component on the client.
- Use tanstackRouterBrowserTracingIntegration so transactions carry the route path template.

## Browser (vanilla)

- Platform id: `browser` · Language: JavaScript · Runs in: web
- Install: `npm install @sentry/browser  # or the loader <script>`

```js
import * as Sentry from "@sentry/browser";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0, release: "my-app@1.4.2" });
```

Lifecycle notes:

- Init as the first script so early errors are captured; window.onerror and unhandledrejection are hooked automatically.

## Node.js

- Platform id: `node` · Language: JavaScript · Runs in: server
- Install: `npm install @sentry/node`
- Files: instrument.mjs (preloaded)

```js
// instrument.mjs — MUST run before any other import (OpenTelemetry patching)
import * as Sentry from "@sentry/node";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// start with:  node --import ./instrument.mjs server.mjs
// Express: after routes, before other error middleware
Sentry.setupExpressErrorHandler(app);
```

Lifecycle notes:

- Init must happen before frameworks/DB drivers are imported — use `node --import ./instrument.mjs` (ESM) or `-r ./instrument.cjs` (CJS) so auto-instrumentation can patch them.
- Express/Fastify/Koa each need their error handler registered AFTER routes: setupExpressErrorHandler(app), setupFastifyErrorHandler(app), setupKoaErrorHandler(app).
- Unhandled rejections are captured by default; process still exits on uncaught exceptions unless you configure onFatalError.

## Hono (Cloudflare Workers)

- Platform id: `hono` · Language: TypeScript · Runs in: edge
- Install: `npm install @sentry/cloudflare`
- Files: src/index.ts

```ts
import * as Sentry from "@sentry/cloudflare";
import { Hono } from "hono";

const app = new Hono();
app.onError((err, c) => { Sentry.captureException(err); return c.text("Internal error", 500); });

// Wrap the Worker so each request gets an isolated scope + trace
export default Sentry.withSentry(
  (env) => ({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0, release: env.RELEASE }),
  app,
);
// wrangler.toml: compatibility_flags = ["nodejs_als"]
```

Lifecycle notes:

- Workers have no long-lived process: withSentry wraps every fetch/queue/scheduled handler so scopes never leak between requests.
- Hono's onError is the catch point for thrown errors; 4xx responses you return yourself are not errors.
- Durable Objects: wrap the class with Sentry.instrumentDurableObjectWithSentry so alarm() and fetch() report.
- Needs the nodejs_als compatibility flag for AsyncLocalStorage-based scope isolation.

## Bun

- Platform id: `bun` · Language: TypeScript · Runs in: server
- Install: `bun add @sentry/bun`

```ts
import * as Sentry from "@sentry/bun";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

Bun.serve({
  fetch(req) { /* ... */ },
  error(err) { Sentry.captureException(err); return new Response("Internal error", { status: 500 }); },
});
```

Lifecycle notes:

- Bun.serve's error() hook is where request errors surface — capture there; unhandled rejections are hooked globally by the SDK.
- Bun's native fetch and Bun.serve are instrumented; Node-compat modules use the Node integrations.

## AdonisJS

- Platform id: `adonis` · Language: TypeScript · Runs in: server
- Install: `npm install @sentry/node`
- Files: start/sentry.ts · app/exceptions/handler.ts

```ts
// bin/server.ts — first import, before the Ignitor boots
import * as Sentry from "@sentry/node";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// app/exceptions/handler.ts
export default class HttpExceptionHandler extends ExceptionHandler {
  async report(error: unknown, ctx: HttpContext) {
    if (this.shouldReport(error as Error)) {
      Sentry.withScope((scope) => { scope.setUser({ id: ctx.auth?.user?.id }); Sentry.captureException(error); });
    }
    return super.report(error, ctx);
  }
}
```

Lifecycle notes:

- The exception handler's report() is the single reporting point for HTTP errors; shouldReport already filters 4xx/validation errors.
- Ace commands and queue jobs run outside HTTP — capture in their catch blocks.

## NestJS

- Platform id: `nestjs` · Language: TypeScript · Runs in: server
- Install: `npm install @sentry/nestjs`
- Files: instrument.ts · app.module.ts

```ts
// instrument.ts — import at the top of main.ts, before NestFactory
import * as Sentry from "@sentry/nestjs";
Sentry.init({ dsn: "https://<public_key>@ingest.bugwatch.io/<project_number>", tracesSampleRate: 1.0 });

// app.module.ts
@Module({ imports: [SentryModule.forRoot()], providers: [{ provide: APP_FILTER, useClass: SentryGlobalFilter }] })
export class AppModule {}
```

Lifecycle notes:

- SentryGlobalFilter must be the FIRST APP_FILTER so your own filters don't swallow exceptions before reporting.
- HttpException subclasses (4xx) are not reported by default.

## Symbolication for this family

Upload maps once per release with `sentry-cli --url https://api.bugwatch.io sourcemaps upload --org <org> --project <project> --release <version> ./dist` (token in `SENTRY_AUTH_TOKEN`), or let the framework plugin (`withSentryConfig`, the Nuxt module, the Vite plugin) do it at build time by pointing its URL option at the Bugwatch API. Details: [Source maps](https://docs.bugwatch.io/sdks/source-maps.md).
