Skip to content

Node.js

Connect your Node server to Tracebeam with one package. It opens an outgoing connection to your Mac—no debug API or inbound server port to expose.

Requires Node.js 22 or later. Open Tracebeam and leave Settings → Connections in Basic mode.

  1. Install from your local tracebeam-node checkout. The package is not published to npm yet:

    Terminal window
    cd /path/to/your-server
    npm install --save-dev /path/to/tracebeam-node
  2. In your Express server, create a client and add its middleware before body parsers and routes. Use your existing Express installation:

    import express from 'express';
    import { createTracebeam } from '@tracebeam/node';
    const tracebeam = createTracebeam({
    appName: 'My API',
    bundleIdentifier: 'com.example.my-api',
    });
    tracebeam.installConsole();
    const app = express();
    app.use(tracebeam.middleware());
    app.use(express.json());
    app.post('/echo', (req, res) => {
    console.info('Echo request received');
    res.json(req.body);
    });
    app.listen(3000);
  3. Run your server with NODE_ENV=development, select its connection in Tracebeam’s center toolbar, and call an endpoint:

    Terminal window
    curl http://localhost:3000/echo -H 'Content-Type: application/json' \
    -d '{"name":"Ada","password":"example-only"}'

Open Network or Console. The response contains the original data; Tracebeam’s JSON preview masks the password.

That’s enough for incoming requests, bodies, and console logs. The default connection is ws://127.0.0.1:49152. No access key, certificate, discovery, or local-network setting is needed for a server running on this Mac.

Already have an Express app? Keep your existing routes and add only the client, console hook, and middleware. CommonJS is also supported: use const { createTracebeam } = require('@tracebeam/node') instead of the import.

The adapter is enabled when NODE_ENV is unset or development. Other values, including test, staging, and production, disable it unless you explicitly set enabled: true. Keep the import in development-only startup if your production install omits devDependencies.

Everything below is for a specific need—not extra steps required to finish the quick start.

Add the hooks you need once, before creating libraries that cache HTTP or fetch functions:

tracebeam.installHttp(); // node:http and node:https request/get calls.
tracebeam.installFetch(); // Global fetch calls; separate from installHttp().

For an individual fetch without a global hook:

const response = await tracebeam.fetch('https://example.com/api', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'Ada' }),
});
const result = await response.json();

Axios using Node’s HTTP transport can be observed through installHttp(). Custom Undici clients, other transports, and function references cached before installation are not automatically intercepted. Tracebeam’s own debugger traffic is excluded.

Use wrapHandler() instead of Express middleware:

const http = require('node:http');
const { createTracebeam } = require('@tracebeam/node');
const tracebeam = createTracebeam({ appName: 'My API' });
http.createServer(tracebeam.wrapHandler((req, res) => {
tracebeam.log('info', 'Request received', { method: req.method });
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({ message: 'Hello' }));
})).listen(3000);

The same handler approach works with a native HTTPS server. The adapter targets Node HTTP/HTTPS and Express-compatible middleware; it does not provide a Fastify or HTTP/2 integration, WebSocket message inspection, database tracing, or state inspectors.

tracebeam.log('info', 'Message', { detail: 'value' }) sends an explicit structured log. Supported levels are debug, info, warn, and error.

installConsole() captures console.log, info, debug, warn, and error while preserving terminal output. Raw stdout/stderr and custom logging transports are not automatically captured. Creating the client alone does not install global hooks.

Each install…() method returns a restore function. Only one client can own each global hook at a time. Call tracebeam.dispose() from your existing shutdown or hot-reload cleanup to restore owned hooks, cancel retries, close the socket, and clear buffers. The adapter does not install signal or crash handlers. Create a new client to start again after disposal.

Body previews are on by default for instrumented requests. Supported content includes JSON, URL-encoded forms, and non-XML text.

Incoming and raw HTTP bodies are observed as your application consumes or writes them. The adapter does not drain unread requests, buffer whole responses, or spool uploads to disk. Headers can appear first, with a later body update in the same network row.

Fetch captures supported string, URLSearchParams, ArrayBuffer, and typed-array request bodies. Streaming, Blob, multipart, and preconstructed Request bodies are not read for capture. Response previews follow application reads through a pull-through stream, without an automatic clone or background drain. The returned standard Response preserves status, headers, URL, redirect metadata, cancellation, and clone behavior, but is not the original object identity when wrapped.

Defaults are 64 KiB per preview and 4 MiB shared raw body buffers, with at most 256 allocated previews. The desktop’s capture setting can lower the per-body limit; Unlimited does not bypass adapter limits.

To customize capture, replace the quick start’s client options:

const tracebeam = createTracebeam({
appName: 'My API',
bundleIdentifier: 'com.example.my-api',
captureRequestBodies: true,
captureResponseBodies: true,
maximumBodyBytes: 64 * 1024,
maximumTotalBodyBytes: 4 * 1024 * 1024,
redactedHeaders: ['X-Device-Signature'],
redactedQueryParameters: ['sessionSecret'],
redactedBodyFields: ['paymentSecret'],
redactedLogFields: ['paymentSecret'],
});

Set either body flag to false to omit that side’s body. Per-body previews can be increased up to 1 MiB, but encoded events must also fit maximumEventBytes (256 KiB by default, up to 1 MiB). Oversized body updates fall back to metadata when it fits. The raw-buffer budget is not a total process-memory limit.

Common credentials and configured field names are masked in captured headers, query parameters, structured logs, JSON, and forms without changing application data. Plain text, URL paths, arbitrary log messages, and unknown custom fields can still contain secrets. Review recordings and exports; redaction is not a universal secret detector.

  • Unread or partially consumed: only observed bytes can be captured. Consume or cancel fetch responses; consume or destroy raw HTTP streams.
  • Unsupported content: binary, multipart, SSE, XML, and still-compressed raw HTTP content are omitted. Native fetch’s already-decompressed response stream can be captured.
  • Incomplete or malformed JSON: omitted when field redaction cannot be guaranteed. Truncated forms exclude the incomplete final field.
  • Capture limits: previews can be truncated or omitted when body, buffer, or queue limits are reached.

Truncated and redacted bodies are not complete API-contract inputs. redactBodyFields: false allows raw previews for deliberately trusted fixtures, not real credentials. Abandoned partial previews remain bounded but can consume capture capacity until disconnect or disposal.

For Docker Desktop for Mac, set the client’s address to the Mac host:

const tracebeam = createTracebeam({
appName: 'My API',
url: 'ws://host.docker.internal:49152',
});

Keep NODE_ENV=development. Enable Settings → Connections → Connect devices over Wi-Fi or Ethernet in Tracebeam and allow its listener through the Mac firewall. Container 127.0.0.1 is the container, not the Mac. No Tracebeam debug port needs publishing from Docker. A local adapter dependency must be included in your build context.

On another computer, use the Mac’s reachable hostname/IP instead of host.docker.internal. Node does not perform automatic discovery. Basic is unencrypted and unauthenticated; keep it on trusted development networks, never the public internet. Use a private tunnel or VPN plus Secure for remote testing.

To make the address configurable, map your own environment variable explicitly:

const tracebeam = createTracebeam({
url: process.env.TRACEBEAM_URL ?? 'ws://127.0.0.1:49152',
});

The adapter does not automatically read TRACEBEAM_URL or credential variables. Apart from the NODE_ENV default, configuration comes from the options you pass.

Choose Secure in Tracebeam’s Connections settings, copy the access key, and export the Device CA Certificate, not the Proxy CA:

const tracebeam = createTracebeam({
url: 'wss://YOUR-MAC.local:49152',
accessKey: process.env.TRACEBEAM_ACCESS_KEY,
deviceCertificateAuthorityPath: '/private/config/Tracebeam-Device-CA.cer',
});

Replace the hostname with one covered by the certificate and use the actual exported file path. PEM and DER are supported. Custom trust applies only to the debugger socket; application TLS and HTTP agents are unchanged. Omit the CA path if Node already trusts the Device CA.

Keep credentials in private development configuration, not source control or container images. In Docker, mount the certificate read-only. Do not disable TLS verification to fix a connection.

With no explicit URL, supplying a key or CA selects wss://. Secure always requires a key. An explicit ws:// URL with credentials is rejected; failed Secure connections never downgrade to Basic. Connection troubleshooting.

Each client instance has a new session ID. Transient reconnects retain that session and its in-memory reconnect proof. A separate random installation ID is stored in user cache storage, scoped to bundleIdentifier (defaults to appName), so the Mac can remember the connection across launches.

For containers, set identityDirectory to a private writable persistent volume. Each replica needs its own directory; do not share IDs, bake them into images, or back them up. Clearing storage creates a new identity. persistInstallationId: false disables persistence.

The sender queue defaults to 512 events / 4 MiB, with one additional event in flight. Configure maximumQueuedEvents, maximumQueuedBytes, and maximumEventBytes if needed. Incoming/raw HTTP capture is limited to 256 concurrent operations; extra requests continue normally without capture.

Body redaction and encoding are deferred with an event-loop yield between events, not moved to a separate worker thread. Capture has bounded memory use but still has CPU overhead. tracebeam.statistics exposes queue, body-buffer, sent, and dropped counts. onStatusChanged(state) provides sanitized connection status.

Capture is best effort: disconnected activity is skipped, full queues drop events, and disconnect clears pending telemetry. SDK-local counters are not forwarded to the Mac’s Capture Health panel. You can record a connected Node session on the Mac, but the adapter has no offline recorder or screen capture, and a recording cannot recover omitted events.

  • No connection: check Tracebeam is listening, mode/port match, and NODE_ENV is unset or development. Docker needs the host address and network access described above.
  • No incoming requests or bodies: put the middleware before body parsers and routes; use wrapHandler() for native HTTP servers. Generate new activity after connecting.
  • No outgoing requests: installHttp() and installFetch() are separate. Install the appropriate hook before libraries cache those functions, or use tracebeam.fetch() directly.
  • No logs: install console capture or call tracebeam.log(); raw stdout and custom logger transports are outside the console hook.
  • Duplicate hook error after hot reload: dispose the old client before installing hooks on a new one.
  • Missing bodies: check consumption, content type, redaction, and body limits.
  • New connection identity after recreating a container: use a private persistent identity directory and stable bundle identifier.
  • TLS errors: use the current Device CA, certificate-matching hostname, and access key. Never substitute the Proxy CA or disable validation.