DebugBundle
SDKs

Node.js SDK

Capture exceptions, logs, HTTP context, and probes from Node.js applications. Express, Fastify, and Next.js integrations included.

Installation

npm install @debugbundle/sdk-node

Quick Setup

import DebugBundle from '@debugbundle/sdk-node';

DebugBundle.init({
  projectToken: 'local',       // 'local' for local-only, or your project token
  environment: 'production',
  service: 'my-api',
});

Calling init() automatically:

  • Hooks process.on('uncaughtException') and process.on('unhandledRejection')
  • Registers SIGINT/SIGTERM/beforeExit handlers for graceful flush
  • Auto-detects and attaches logger integrations (pino, winston, bunyan)
  • Starts remote config polling for capture policy, capture rules, and probes

Backend exception events include safe runtime process facts in payload.runtime: Node version, platform, architecture, process id, working directory, process uptime, hostname, and best-effort memory stats. The SDK does not read or emit environment variables for this block.

Configuration

OptionTypeDefaultDescription
projectTokenstringRequired. Use 'local' for local-only mode, or your project token for connected mode. If empty, SDK is disabled.
environmentstringprocess.env.NODE_ENV ?? "development"Environment name. Controls transport selection.
servicestringAuto-detected from package.json nameService identifier. Falls back to "node-service".
frameworkstring | nullAuto-detected"express", "fastify", or "nextjs". Auto-detected from dependencies.
projectMode"connected" | "local-only""connected""local-only" uses file transport exclusively.
localEventsDirstring.debugbundle/local/eventsFile transport destination directory.
enabledbooleantrueKill switch. Set false to fully disable capture.
redactFieldsstring[]["password", "secret", "token", "authorization", "cookie", "ssn", "credit_card"]Field names to redact from captured data.
sampleRatenumber (0–1)1.0Event-level sampling rate. 0.5 = capture 50% of events.
logLevelLogLevel"warning"Minimum log level to capture: "debug", "info", "warning", "error", "critical".
batchSizenumber50Maximum events per flush batch.
flushIntervalnumber (ms)2000Auto-flush interval.
maxBufferedEventsnumber1000Maximum events in buffer. Oldest dropped when full.
endpointstring"https://api.debugbundle.com/v1/events"Ingestion API endpoint.
requestTimeoutMsnumber (ms)5000HTTP transport timeout.
captureConsolebooleanfalseOpt-in: capture console.error and console.warn.
autoDetectLoggersbooleantrueAuto-detect and attach pino/winston/bunyan on init.
loggerunknownPass an existing logger instance to attach on init.
probesPollIntervalnumber (ms)60000Remote probe config poll interval.
maxProbeLabelsnumber50Maximum distinct probe labels.
maxProbeEntriesPerLabelnumber10Ring buffer size per probe label.
probeFlushOnErrorbooleantrueFlush probe buffers alongside exceptions.
transportDebugBundleTransportAuto-selectedCustom transport override.
fetchImpltypeof fetchglobalThis.fetchCustom fetch implementation for HTTP transport.
onDiagnostic(diagnostic) => voidCallback for SDK self-diagnostic events.

Transport Selection

The SDK automatically picks the right transport:

projectModeEnvironmentTransportDestination
connectedlocal, developmentFile.debugbundle/local/events/
connectedstaging, productionHTTPPOST /v1/events
local-onlyAnyFile.debugbundle/local/events/

File transport writes atomic files: <timestamp>-<sequence>-<service>.events.json.

Server-side SDKs are not controlled by browser CSP. In connected staging and production, the practical requirement is outbound network access from your runtime to the configured DebugBundle API endpoint. In locked-down environments, that usually means allowing HTTPS egress, proxy routing, or an internal self-hosted endpoint.


Framework Integrations

Express

import DebugBundle from '@debugbundle/sdk-node';
import express from 'express';

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-api',
});

const app = express();

// Add DebugBundle middleware — captures request/response context
app.use(DebugBundle.express());

app.get('/api/users', (req, res) => {
  res.json({ users: [] });
});

// Error handler — DebugBundle captures the error with full request context
app.use((err, req, res, next) => {
  res.status(500).json({ error: 'Internal server error' });
});

app.listen(3000);

The Express middleware:

  • Hooks res.end() to capture request/response pairs
  • Uses runWithRequestContext() for async correlation
  • Auto-detects loggers from req.app.locals.logger, req.logger, or req.log
  • Captures exceptions passed to next(error)

Fastify

import DebugBundle from '@debugbundle/sdk-node';
import Fastify from 'fastify';

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-api',
});

const app = Fastify({ logger: true });

// Register DebugBundle plugin — auto-hooks pino logger
app.register(DebugBundle.fastify());

app.get('/api/users', async (request, reply) => {
  return { users: [] };
});

app.listen({ port: 3000 });

The Fastify plugin:

  • Uses onRequest, onResponse, and onError hooks
  • Automatically hooks Fastify's built-in pino logger
  • Correlates requests via runWithRequestContext()

Next.js

// pages/api/users.ts (or app route handler)
import DebugBundle from '@debugbundle/sdk-node';

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-frontend-api',
});

export default DebugBundle.nextjs(async (req, res) => {
  res.json({ users: [] });
});

The Next.js wrapper catches thrown errors and calls captureException with the full request context.


Logger Integrations

The SDK auto-detects and patches your existing logger on init(). Detection priority: pino → bunyan → winston.

pino

import pino from 'pino';
import DebugBundle from '@debugbundle/sdk-node';

const logger = pino();

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-api',
  logger: logger,  // or let autoDetectLoggers find it
});

// These log calls are now captured by DebugBundle
logger.info('Server started');
logger.error({ err: new Error('fail') }, 'Request failed');

winston

import winston from 'winston';
import DebugBundle from '@debugbundle/sdk-node';

const logger = winston.createLogger({
  transports: [new winston.transports.Console()],
});

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-api',
  logger: logger,
});

logger.error('Something went wrong', { userId: '123' });

bunyan

import bunyan from 'bunyan';
import DebugBundle from '@debugbundle/sdk-node';

const logger = bunyan.createLogger({ name: 'my-api' });

DebugBundle.init({
  projectToken: 'local',
  environment: 'production',
  service: 'my-api',
  logger: logger,
});

logger.error(new Error('fail'), 'Request failed');

Level mapping for all loggers:

Logger levelDebugBundle level
tracedebug
debugdebug
infoinfo
warn / warningwarning
errorerror
fatalcritical

The original logger behavior is preserved — DebugBundle patches call the original method first, then capture.


Manual Capture

Use manual capture when you want to report a handled failure instead of only relying on unhandled exceptions. In connected mode, captureException() and captureError() create or update incidents through the normal ingestion pipeline.

Exceptions

try {
  await riskyOperation();
} catch (error) {
  DebugBundle.captureException(error, {
    request: req,
    response: res,
    tags: { component: 'payment' },
  });
}

This is the recommended way to surface a handled failure from a catch block. If you have alert rules for new_incident, the resulting incident can trigger notifications just like an unhandled exception.

Logs

DebugBundle.captureLog('User signup completed', 'info', {
  tags: { flow: 'onboarding' },
});

Only incident-level log severities such as error, fatal, or critical create incidents. Informational and warning logs remain contextual unless another incident-driving event exists.

HTTP Requests

DebugBundle.captureRequest(
  { method: 'POST', url: '/api/charge', headers: req.headers, body: req.body },
  { statusCode: 500, headers: res.getHeaders(), body: responseBody },
);

Context

// This context attaches to all future events
DebugBundle.setContext('user', { id: '123', plan: 'team' });
DebugBundle.setContext('deploy', { version: '1.2.3', region: 'us-east-1' });

Probes

Probes are always-on diagnostic ring buffers. Data is captured locally and flushes alongside exceptions.

// Always-on probe — runs on every request (all tiers)
DebugBundle.probe('db-query-timing', {
  query: 'SELECT * FROM users',
  duration: 142,
  rows: 50,
});

// Lazy probe — callback only invoked when needed
DebugBundle.probe('memory-snapshot', () => ({
  heapUsed: process.memoryUsage().heapUsed,
  rss: process.memoryUsage().rss,
}));

// Heavy probe — only runs when remotely activated (paid tiers)
DebugBundle.probe('full-request-dump', () => ({
  headers: req.headers,
  body: req.body,
  timing: performanceTiming,
}), { heavy: true });

Each probe label maintains a ring buffer of the last 10 entries (configurable via maxProbeEntriesPerLabel). When an exception fires, all probe buffers are flushed alongside the error event and attach to the resulting debug bundle as context.probe_data.


Volume Control

The SDK enforces automatic storm suppression:

ControlThresholdBehavior
Duplicate dedup3 events in 30sIdentical events beyond the 3rd are suppressed
Loop detection10+ events in 2sEnters suppression mode
Suppression checkpointEvery 30sOne aggregate error_suppressed event
Auto-recovery60s silenceResets to normal capture

Combined with server-side capture policy and project capture rules, you always have layered control over event volume. The Node.js SDK locally discards events matching drop rules and sampled-out sample rules before buffering. demote rules are enforced by ingestion and worker processing so matching backend events cannot create or reopen incidents.


Browser Relay

The Node.js SDK includes relay handlers that proxy browser events from your frontend. See Browser Relay Setup for details.

Available as subpath exports:

// Express
import { debugBundleRelay } from '@debugbundle/sdk-node/relay/express';
app.use('/debugbundle/browser', debugBundleRelay());

// Fastify
import { debugBundleRelayPlugin } from '@debugbundle/sdk-node/relay/fastify';
app.register(debugBundleRelayPlugin);

// Next.js
import { debugBundleRelay } from '@debugbundle/sdk-node/relay/nextjs';
export const POST = debugBundleRelay;

Next Steps

  • Browser SDK — Client-side capture with breadcrumbs, network, and session controls
  • Browser Relay Setup — Configure the relay handler connecting browser → backend
  • SDKs Overview — Universal interface and language support matrix

On this page