BugBoardDocs

Framework guides

The JavaScript and PHP SDK pages cover the three steps every integration shares: install, configure a shared client, call a severity method. These guides pick up where those leave off — where the client is built in each framework, which key it uses, where you hook it into the framework's error handling, and where the queued reports actually get delivered.

Pick your framework

PHP JavaScript / TypeScript
Laravel Node: Express, Fastify, Koa
Symfony NestJS
Plain PHP — Slim, CodeIgniter, WordPress, CLI Next.js
Nuxt
SvelteKit
Remix / React Router
Vite SPA: React, Vue, Svelte
Serverless & edge — Lambda, Vercel, Workers, Deno

Using something not listed? For PHP, the plain PHP guide applies to any framework without a dedicated integration. For JavaScript there is no framework-specific package at all — import { createClient } from 'bugboard' is the entire surface on every runtime, so the closest guide above will get you there.

What every integration has in common

1. Build one client and share it. The client owns a buffer (and in JavaScript, a drain timer and a shutdown hook). Building one per report gives you N buffers and N hooks. Use a container binding, a module-level export, or a singleton — the point is that there is one.

2. Pick the key that matches where the code runs. Servers use a secret key (keyId + signingSecret, HMAC-signed, the secret never travels on the wire). Browsers use a publishable key (apiKey, bb_pub_…, public by design and write-only). See Choosing a key type.

3. Reporting never blocks and never throws. A reporting call validates, clamps and buffers the report, then returns. Delivery failures surface on the debug channel, never as an exception in your app.

4. Titles must be stable. Reports are deduplicated server-side by title, so 'Stripe webhook verification failed' is one card with a climbing occurrence count, while "Webhook {$id} failed at " . now() is a new card every request. Put the variable parts in the description or the tags. See Deduplication.

The one thing that differs: where reports are delivered

Reports are buffered, not sent at call time. What drains the buffer is the only genuinely framework-specific concern, and it is what each guide below spends most of its words on.

Runtime What drains the buffer Do you need an explicit flush()?
Laravel (any SAPI) The terminating phase, after the response is sent No
Symfony The SDK's shutdown hook — before the response under FPM Yes — add a kernel.terminate listener
Plain PHP under FPM The SDK's shutdown hook, on the request path Recommended, after fastcgi_finish_request()
Long-running Node server process.on('beforeExit') No
Browser pagehideflushSync() with keepalive No, but flush before a deliberate navigation
Serverless / edge Nothing Yes — always, before returning
Queue workers & daemons (any language) Only when the process finally exits Yes — flush per unit of work

The failure mode is the same everywhere: reports are queued, the process goes away, nothing was sent, and there is no error to tell you so. If reports are not appearing, this table is the first place to look — followed by turning on debug.