BugBoardDocs

JavaScript / TypeScript SDK

Report errors to BugBoard in three steps: install, copy one setup file, then call it.

Source on GitHub: github.com/bug-board/bugboard-js Runs on Node 20+, browsers, and edge runtimes with nothing to install or wire up (it uses the platform fetch). The default path loads no dependencies; opt-in payload encryption lazy-loads a tiny tweetnacl sealed-box binding (tweetnacl-sealedbox-js) that ships bundled with the SDK — it loads automatically when you set an encryption key, with nothing extra to install. See Encrypting sensitive reports.

1. Install

npm i bugboard

2. Add the setup file

Create one file that configures a shared client, then import it anywhere. This keeps your keys in a single place. Pick the tab for where the client runs:

// utils/bugboard.ts
import { createClient } from 'bugboard';

// In the browser or a mobile app, use a publishable key, sent as a bearer token.
export default createClient({
    apiKey: import.meta.env.VITE_BUGBOARD_API_KEY, // bb_pub_…

    // OPTIONAL — if provided, every payload is encrypted before it leaves the client.
    // encryptionKeyId: import.meta.env.VITE_BUGBOARD_ENCRYPTION_KEY_ID,         // bbek_…
    // encryptionPublicKey: import.meta.env.VITE_BUGBOARD_ENCRYPTION_PUBLIC_KEY, // base64 X25519
});
// utils/bugboard.ts
import { createClient } from 'bugboard';

// On a server, use a secret key; requests are HMAC-signed and the secret never leaves the server.
export default createClient({
    keyId: process.env.BUGBOARD_KEY_ID, // bbk_…
    signingSecret: process.env.BUGBOARD_SIGNING_SECRET, // bb_sec_…

    // OPTIONAL — if provided, every payload is encrypted before it leaves the server.
    // encryptionKeyId: process.env.BUGBOARD_ENCRYPTION_KEY_ID,         // bbek_…
    // encryptionPublicKey: process.env.BUGBOARD_ENCRYPTION_PUBLIC_KEY, // base64 X25519

    // OPTIONAL — log reports locally instead of sending them (local debugging / dry run).
    // logLocally: true,
});

Set the matching values in your .env:

VITE_BUGBOARD_API_KEY=bb_pub_xxxxxxxx
BUGBOARD_KEY_ID=bbk_xxxxxxxx
BUGBOARD_SIGNING_SECRET=bb_sec_xxxxxxxx

3. Use it

Call a severity method with a title (required); optionally pass a description — a string or the caught error — and tags.

import bugboard from '@/utils/bugboard';

try {
    await payments.charge(order);
} catch (err) {
    bugboard.criticalHigh('Payment failed', err, ['payment', 'backend']);
}

The title is all you need:

bugboard.major('Checkout is slow');

That's it — the call returns immediately and the report is delivered in the background.

Common methods

Most apps only need these four (medium priority); each severity also has …Low and …High variants — criticalHigh, minorLow, and so on:

bugboard.critical('Payment failed', err); // highest severity
bugboard.major('Checkout is slow', err);
bugboard.moderate('Slow image upload', err);
bugboard.minor('Tooltip misaligned');

Tags are an array or a comma-separated string:

bugboard.critical('Payment failed', err, ['payments', 'checkout']);
bugboard.critical('Payment failed', err, 'payments,checkout');

Where cards point

Every card records the code it is about. Pass the caught error as the description and it names where that error was thrown, not where you reported it:

bugboard.criticalHigh(err.message, err); // → src/checkout/charge.ts
bugboard.major('Cart total went negative'); // → wherever you called from

That is what makes one global handler workable: without it, every error in the app would report the file your handler lives in.

Bundled apps need the build plugin

Once a bundler minifies your code, the call stack only names a chunk — cards read assets/index-a3f2b1.js. Adding the build plugin writes the real file and line in at compile time, with no source maps generated, deployed, or uploaded:

// vite.config.ts
import bugboard from 'bugboard/vite';

export default { plugins: [bugboard()] };

bugboard/rollup is the same plugin; bugboard/webpack is a loader (and works under Turbopack); bugboard/esbuild covers Lambda, Wrangler and SST bundles. JSX/TSX and Vue/Svelte single-file components are all handled.

For a bundled Node server, add import 'bugboard/preload' as the very first import instead — it switches on Node's own source-map support, which reads the .js.map files already sitting next to your chunks.

Line numbers are opt-in

Cards carry file_name by default but not line_number. The file is dependable everywhere; the line is only exact once the build plugin writes it in, and otherwise it is inferred from the call stack and can point at the wrong statement. A line that is almost right reads as authoritative and sends people to the wrong place, so you switch it on deliberately:

createClient({ apiKey: '…', lineNumbers: true });

Install the plugin and turn this on in the same commit — that is the combination that makes both fields exact in production.

Full reference: SOURCE_LOCATIONS.md covers every mechanism, all the plugin options, setup per bundler and framework, and how to turn any of it off (captureLocation: false, sourceMaps: false).

Serverless / short-lived scripts

Reports deliver in the background, and the SDK flushes automatically on shutdown (Node beforeExit, browser pagehide). In environments without lifecycle hooks — lambdas, edge functions, CLI scripts — flush before returning:

await bugboard.flush();

Framework guides

The SDK is identical on every runtime — what changes is where you build the client, which key it uses, and where you flush. These guides cover each one end to end:

Server Full-stack Client
Express, Fastify, Koa Next.js Vite SPA: React, Vue, Svelte
NestJS Nuxt
Serverless & edge SvelteKit
Remix / React Router

In an app with both a server and a client bundle, make two modules — one with the secret key that never reaches the browser, one with the publishable key that does. Adopt whatever server-only convention your framework enforces (.server.ts in SvelteKit and Remix, server-only in Next.js) so an accidental client import fails at build time rather than shipping your signing secret.

Next steps

  • Framework guides — detailed integration for every major framework.
  • API Reference — config options, every severity/priority variant (criticalHigh, …), and the full HTTP contract.
  • Source locations — the build plugin, source maps, and line numbers in full.
  • Encrypting sensitive reports — keep the payload out of the browser network tab with encryptionPublicKey (the bundled tweetnacl-sealedbox-js binding auto-loads for you).
  • Source on GitHub — the full SDK implementation.