JavaScript & TypeScript
The reference BugWatch SDK: one package for Node services, browsers, edge runtimes and every major JS framework, with first-class TypeScript types.
Requirementslink
- Node 20.19+ for server use; evergreen browsers; ships dual ESM and CommonJS builds
- Edge runtimes (Cloudflare Workers, Vercel Edge) use the core client and must not import
./node(no AsyncLocalStorage there); Bun and Deno get core plus the Hono adapter - All framework and logger peers are optional. Minimums: express 4, fastify 4, koa 2, hono 4, hapi 20, nest 9, react 16.8, vue 3, pino 8, winston 3, bunyan 1.8, log4js 6, @opentelemetry/api 1.4
1 - Installlink
npm install @newinstance/bugwatchThe credential is the project DSN key (sk_test_… or sk_live_…, one per environment) from the dashboard project's Settings under DSN keys.
2 - Initialiselink
Server (Node):
import { BugWatch } from "@newinstance/bugwatch";
import { installNodeErrorHandlers } from "@newinstance/bugwatch/node";
const client = BugWatch.init({
projectKey: process.env.BUGWATCH_KEY!,
release: process.env.APP_VERSION,
});
installNodeErrorHandlers(client);init is idempotent (a singleton on globalThis, safe under hot reload) and returns the existing client on repeat calls; captures before init are no-ops that still return a generated id. installNodeErrorHandlers is idempotent and returns an uninstall function; installAsyncScope + runWithContext give per-unit isolation to non-framework code (queues, workers).
Browser (never ship the key):
import { createClient } from "@newinstance/bugwatch";
import { installBrowserErrorHandlers } from "@newinstance/bugwatch/browser";
const client = createClient({ sessionUrl: "/bugwatch/session" });
installBrowserErrorHandlers(client);Your backend exposes the mint endpoint: Express has a ready-made bugWatchBrowserSessionHandler; anything else calls mintBrowserSession which returns { token, expiresAt }. The transport refreshes the token 30 seconds before expiry and on a 401.
3 - Configurationlink
| Option | Default | What it does |
|---|---|---|
projectKey / sessionUrl | - | Server credential, or the browser mint endpoint |
release / environment | - / from key | Labels on every event |
enabled / debug | true / false | Master switch; debug prints delivery diagnostics including the server's reason string after the HTTP status |
sampleRate | 1 | Applied before beforeSend; keep-all-errors sampling belongs in beforeSend |
captureUnhandledErrors / captureUnhandledRejections | true | Global handlers, chained not replaced |
sensitiveFields | built-in list | Extra redaction keys |
beforeSend | - | May be async; return null to drop, or mutate and return the event |
console | on | Local echo of the native logger: false or `{ enabled, level, format: "auto" |
batchSize / flushInterval | 50 / 5000 ms | Delivery batching; flushInterval: 0 disables the timer (manual flush only) |
maxQueueSize | 1000 | Overflow drops the oldest queued events |
requestTimeout | 15000 ms | Per-request timeout |
retry | 3 attempts, 200 ms to 5 s | Exponential backoff |
traceContextProvider | - | Auto-attach traceId and spanId (OTel below) |
4 - Capture APIlink
const id = BugWatch.captureException(err, { tags: { route: "/checkout" } });
BugWatch.captureMessage("Payment settled", 30);
BugWatch.captureLog({ level: "warn", message: "Slow query", tags: { db: "orders" } });
BugWatch.setUser({ id: "u_123", email: "ada@example.com" });
BugWatch.setTag("tenant", "acme");
BugWatch.setContext("payment", { provider: "paystack" });
BugWatch.setRelease("checkout@2.4.1");
await BugWatch.flush();
await BugWatch.close();- Levels are Pino-compatible numerics 10 to 60 (the
LEVELSmap is exported);captureLogtakes one object. - The per-capture hint accepts
level,tags,user,traceId,spanId. - Call
await BugWatch.close()on SIGTERM so the queue drains before exit.
5 - Framework adapterslink
Every adapter accepts a getUser option, resolved lazily at capture time, which replaces the auth-middleware pattern entirely; an explicit setRequestUser still wins. Adapters never swallow errors: they capture, then rethrow or call next(err).
| Stack | Subpath | Entry points and notes |
|---|---|---|
| Express | ./express | Request handler first, error handler last; bugWatchBrowserSessionHandler for the mint route |
| Fastify / Koa / Hono / Hapi | ./fastify etc. | bugWatchFastify, bugWatchKoa, bugWatchHono (pass opts.context to read Hono's c), createBugWatchHapiPlugin (identity read at onPreResponse) |
| NestJS | ./nest | BugWatchExceptionFilter, bugWatchNestMiddleware |
| Next.js and serverless | ./next | withBugWatchRouteHandler opens a per-request scope and flushes before return; the same wrapper pattern serves Lambda, Vercel and Netlify functions |
| React / Vue | ./react, ./vue | BugWatchErrorBoundary (captures render errors with component stack; renders nothing without a fallback), createBugWatchVuePlugin (preserves an existing errorHandler) |
6 - Logger integrationslink
| Logger | Factory |
|---|---|
| Native | createLogger() with child(bindings), zero deps, echoed locally per the console option |
| Pino | createBugWatchPinoDestination (only scalar fields become tags; objects and arrays are dropped) |
| Winston | createBugWatchWinstonTransport (error 50, warn 40, info 30, verbose and debug 20, silly 10) |
| Bunyan / log4js | createBugWatchBunyanStream, bugWatchLog4jsAppender |
| console | captureConsole(client, { levels }), returns a restore() |
7 - Testinglink
import { InMemoryTransport } from "@newinstance/bugwatch/testing";
const transport = new InMemoryTransport();
const client = new BugWatchClient(opts, { transport });
transport.events; transport.find(pred); transport.reset();8 - Readable stack traceslink
Node stacks are readable as-is. React Native bundles resolve through the source map you upload in CI (see Mobile → React Native). For minified web bundles, server-side source-map resolution is not available yet: keep source maps deployed next to your bundles so browser devtools resolve frames.
Production checklistlink
await BugWatch.close()on SIGTERM;installNodeErrorHandlersat boot.- Keep the DSN key out of the repo and rotate it from the dashboard on exposure.
- Browsers get
sessionUrl, neverprojectKey.
Troubleshootinglink
- 401 Invalid or inactive API key or 400 This API key is not a BugWatch project key: you are using an org key; use the project DSN key from Settings under DSN keys.
- Events missing from a short-lived script: the batch timer never fired; call
await BugWatch.flush(). - Pino fields missing as tags: only string, number, boolean and bigint values map to tags.
Wire endpoints used: POST /api/v1/bugwatch/ingest (NDJSON, up to 5000 events per request), POST /api/v1/bugwatch/browser-session, POST /api/v1/bugwatch/ingest/browser.