bugwatch docs

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 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
// 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
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
// 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
// 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
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
// 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
// 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>
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)
// 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
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
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
// 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
// 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.