Frameworks

Breadcrumb’s handler is a single fetch-native function — it takes a Request and returns a Response, and it serves the JSON API your traces are read and written through. You mount it as a catch-all at your basePath so it receives every method and sub-path underneath. Pick your framework below for the exact setup.

IMPORTANT

The route you mount at must match the basePath you passed to breadcrumb(). Every example uses /api/breadcrumb.

The handler serves data, not HTML. To look at your traces, mount the dashboard component at a separate route in your frontend and point it at this one.

The shared instance

Each guide imports one instance you create once and reuse:

// lib/breadcrumb.ts
import { breadcrumb } from "@breadcrumb-sh/core";
import { postgres } from "@breadcrumb-sh/core/adapters";

export const bc = breadcrumb({
  database: postgres(process.env.DATABASE_URL!),
  basePath: "/api/breadcrumb",
  authorize: (req) => isAdmin(req),
});

Protecting the API

The query routes are unauthenticated by default. Guard them with the authorize option — it runs on every query request, but not on the routes that carry their own key:

breadcrumb({
  authorize: (req) => isAdmin(req), // true to allow, false for 401, or a Response
});

You can wrap the mount in your framework’s own auth middleware instead, but scope it. Two route families authenticate themselves, because their callers have no browser session to present:

RouteAuthenticates with
/api/ingest/*your ingest.apiKey
/api/mcpan MCP key

Middleware over the whole mount runs before Breadcrumb sees the request, so it turns away your trace exporters and your coding agent along with everyone else. Exclude those two paths, or use authorize, which Breadcrumb applies only after the self-authenticating routes have had their turn.

The route your dashboard page lives on is yours, so guard it the way you guard any other page in your app. authorize protects the data; your own auth protects the page.

See Production for the full checklist.