JavaScript & TypeScript
For agents: paste the DSN from
list_keysinto 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
releasein 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 exposesX-Sentry-Rate-Limits/Retry-Afterso 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.