Let AI set up TraceKit for you

AI assistants can guide you through the entire setup process

Try AI Setup

Node.js / TypeScript Integration Guide

Learn how to instrument your Node.js and TypeScript applications with TraceKit APM for zero-config distributed tracing.

Using Express?

See our dedicated Express observability guide for framework-specific setup, pain points, and best practices.

Node.js Distributed Tracing Guide

Go deeper with our Node.js distributed tracing guide -- covering common pain points, production patterns, and code examples.

New: Frontend Observability

Looking for browser-side error tracking? Check out the new Browser SDK with React, Vue, Angular, Next.js, and Nuxt integrations via Framework Wrappers.

Zero-Config Automatic Tracing!

TraceKit Node APM package automatically traces HTTP requests, errors, and provides first-class TypeScript support. Just install, configure, and go!

Prerequisites

  • • Node.js 16.x or higher
  • • Express 4.x/5.x or NestJS 10.x
  • • A TraceKit account (create one free)
  • • A generated API key from the API Keys page

What Gets Traced Automatically?

With TraceKit Node APM installed, these operations are traced automatically:

ComponentSpan TypeCaptured AttributesAuto-Traced?
Incoming HTTPSERVERroute, method, status, duration, client_ip, user_agentYes
Outgoing HTTPCLIENTmethod, url, status_code, duration, peer.serviceYes
PostgreSQLDBdb.system, db.statement, db.name, durationYes
MySQLDBdb.system, db.statement, db.name, durationYes
MongoDBDBdb.system (mongodb), db.operation, db.name, durationYes
RedisDBdb.system (redis), db.statement, durationYes
LLM (OpenAI/Anthropic)CLIENTgen_ai.system, model, tokens, cost, finish_reason, latencyYes
ExceptionsN/Atype, message, stack_trace, request_contextYes
Custom SpansCustomuser-defined attributesManual

Auto-Instrumented Libraries

The following libraries are automatically instrumented when detected. Import them after calling tracekit.init() for automatic span creation:

Library Span Type Captured Attributes Auto-Instrumented?
pg (PostgreSQL) DB db.system, db.statement, db.name, duration ✓ Yes
mysql / mysql2 DB db.system, db.statement, db.name, duration ✓ Yes
mongodb DB db.system (mongodb), db.operation, db.name, duration ✓ Yes
ioredis / redis DB db.system (redis), db.statement, duration ✓ Yes
node:http / node:https CLIENT http.method, http.url, http.status_code, peer.service ✓ Yes
undici (Fetch API) CLIENT http.method, http.url, http.status_code, peer.service ✓ Yes

Note: Import database and HTTP client libraries after calling tracekit.init() to ensure automatic instrumentation is applied.

Installation

Install the TraceKit Node APM package via npm or yarn:

npm install @tracekit/node-apm # or yarn add @tracekit/node-apm

Express Setup

JavaScript

const express = require('express');
const tracekit = require('@tracekit/node-apm');

const app = express();

// Initialize TraceKit (before routes!)
tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY,
  serviceName: 'my-express-app',
  endpoint: 'https://app.tracekit.dev/v1/traces',
  enableCodeMonitoring: true, // Optional: Enable live debugging
});

// Add TraceKit middleware (before routes!)
app.use(tracekit.middleware());

// Your routes - automatically traced!
app.get('/api/users', (req, res) => {
  res.json({ users: ['alice', 'bob'] });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

TypeScript

import express from 'express';
import * as tracekit from '@tracekit/node-apm';

const app = express();

// Initialize TraceKit (before routes!)
tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY!,
  serviceName: 'my-express-app',
  endpoint: 'https://app.tracekit.dev/v1/traces',
  enableCodeMonitoring: true, // Optional: Enable live debugging
});

// Add TraceKit middleware (before routes!)
app.use(tracekit.middleware());

// Your routes - automatically traced!
app.get('/api/users', (req, res) => {
  res.json({ users: ['alice', 'bob'] });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Verify It Works

Start your application and make a few requests. Then open the Traces page in your TraceKit dashboard. You should see:

  • • SERVER spans for each incoming HTTP request with route and status
  • • CLIENT spans for outgoing HTTP calls (auto-traced for http/https, fetch, axios)
  • • DB spans for database queries (auto-traced for PostgreSQL, MySQL, MongoDB, Redis)

Traces typically appear within 30 seconds. If you don't see them, check the Troubleshooting section.

NestJS Setup

1. Import TracekitModule

Add the TracekitModule to your app.module.ts:

// app.module.ts
import { Module } from '@nestjs/common';
import { TracekitModule } from '@tracekit/node-apm/nestjs';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TracekitModule.forRoot({
      apiKey: process.env.TRACEKIT_API_KEY!,
      serviceName: 'my-nestjs-app',
      endpoint: 'https://app.tracekit.dev/v1/traces',
      enableCodeMonitoring: true, // Optional: Enable live debugging
    }),
    UsersModule,
  ],
})
export class AppModule {}

2. Async Configuration

For environment-based configuration using ConfigModule:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TracekitModule } from '@tracekit/node-apm/nestjs';

@Module({
  imports: [
    ConfigModule.forRoot(),
    TracekitModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        apiKey: config.get('TRACEKIT_API_KEY')!,
        serviceName: config.get('APP_NAME', 'my-app'),
        endpoint: config.get('TRACEKIT_ENDPOINT'),
        enabled: config.get('NODE_ENV') === 'production',
        enableCodeMonitoring: true, // Optional: Enable live debugging
      }),
    }),
  ],
})
export class AppModule {}

Automatic Interceptor

The TracekitModule automatically registers a global interceptor that traces all HTTP requests. No additional middleware needed!

Middleware & Interceptor API

Framework integration functions and classes for automatic request tracing:

Function / Class Parameters Returns Description
createExpressMiddleware() client: TracekitClient
snapshot?: boolean
RequestHandler Creates Express middleware that automatically traces all incoming HTTP requests. Optionally enables snapshot capture on the request object.
getCurrentSpan() req: Request Span | undefined Retrieves the current active span from an Express request. Returns undefined if no span is active.
getClientIp() req: Request string | undefined Extracts client IP from request headers (X-Forwarded-For, X-Real-IP) with proxy support.
TracekitInterceptor NestJS class NestInterceptor NestJS interceptor for automatic span creation on all controller methods. Registered globally by TracekitModule.

Code Monitoring (Live Debugging)

Production-Safe Live Debugging

Capture variable state, stack traces, and request context at any point in your code without redeployment. Perfect for debugging production issues!

→ Full Code Monitoring Documentation

Setup

Enable code monitoring when initializing TraceKit:

import * as tracekit from '@tracekit/node-apm';

const client = tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY!,
  serviceName: 'my-app',
  enableCodeMonitoring: true,  // Enable live debugging
});

Usage: Add Debug Points

Use client.captureSnapshot() to capture variable state:

app.post('/checkout', async (req, res) => {
  const { userId, amount } = req.body;

  // Capture snapshot at this point
  await client.captureSnapshot('checkout-validation', {
    userId,
    amount,
    timestamp: new Date().toISOString(),
  });

  // Your business logic...
  const result = await processPayment(amount, userId);

  // Another checkpoint
  await client.captureSnapshot('payment-complete', {
    userId,
    paymentId: result.paymentId,
    success: result.success,
  });

  res.json(result);
});

Features

Auto-Registration

Breakpoints automatically created on first call

Variable Capture

Deep inspection of objects, arrays, and primitives

Stack Traces

Full call stack with file and line numbers

Request Context

HTTP method, route, headers, and query params

Production Safety

v6.0 adds multiple layers of protection so code monitoring is safe for production workloads:

PII Scrubbing

13 built-in sensitive-data patterns enabled by default. Matched values are replaced with typed [REDACTED:type] markers before data leaves the process.

Crash Isolation

Every capture entry point is wrapped in try/catch so a failure in the SDK never propagates to your application code.

Circuit Breaker

After 3 consecutive failures within 60 seconds the SDK enters a 5-minute cooldown, then auto-recovers. Fully configurable thresholds.

Remote Kill Switch

Toggle code monitoring on or off from the TraceKit dashboard. The change propagates to all connected SDK instances via SSE in real time.

Real-Time Updates via SSE

The Node SDK opens a lightweight Server-Sent Events connection to the TraceKit server on startup. Breakpoint enables/disables, kill-switch toggles, and configuration changes are pushed instantly — no polling delay. The SSE endpoint is auto-discovered from /sdk/snapshots/auto-register so no extra config is needed.

Exception Handling

Exceptions are automatically captured with full stack traces for code discovery:

// Exceptions in spans are automatically captured
app.get('/api/users/:id', async (req, res, next) => {
  try {
    const user = await getUserById(req.params.id);
    res.json(user);
  } catch (error) {
    // TraceKit automatically captures exception with stack trace
    next(error);
  }
});

// Express error handler
app.use((err, req, res, next) => {
  client.recordException(null, err);
  res.status(500).json({ error: 'Internal Server Error' });
});

Production Safe

Snapshots are sampled and have minimal performance impact. Capture rates and conditions can be controlled via the TraceKit dashboard.

Code Monitoring API

Programmatic API for code monitoring and snapshot capture:

Method / Class Parameters Returns Description
captureSnapshot() label: string — breakpoint label
vars: object — variables to capture
Promise<void> Captures a snapshot of variable state at the call site. Auto-registers a breakpoint on first call. Rate-limited to 1 capture/sec per label.
getSnapshotClient() none SnapshotClient | undefined Returns the snapshot client if code monitoring is enabled, or undefined if disabled. Use to guard snapshot calls.
SnapshotClient class Advanced / Internal. Manages breakpoint polling and snapshot uploads. Created automatically when enableCodeMonitoring: true.
CaptureConfig interface Configuration for code monitoring capture behaviour. Fields: enablePiiScrubbing (boolean, default true), piiPatterns (PIIPatternEntry[]), circuitBreaker (CircuitBreakerConfig).
CircuitBreakerConfig interface Configures circuit breaker thresholds. Fields: maxFailures (number, default 3), windowMs (number, default 60000), cooldownMs (number, default 300000).
PIIPatternEntry interface Defines a sensitive-data pattern for PII scrubbing. Fields: name (string — pattern label), pattern (RegExp — detection regex), redactedType (string — marker used in [REDACTED:type]).

End-to-End Workflow

1

Enable Code Monitoring

Code monitoring defaults to disabled in the Node.js SDK. You must explicitly enable it in your config.

const client = new TracekitClient({
  apiKey: process.env.TRACEKIT_API_KEY,
  serviceName: 'my-express-app',
  enableCodeMonitoring: true,  // default: false
});
2

Add Capture Points

Place captureSnapshot calls at code paths you want to debug. Note: captureSnapshot returns a Promise — use await.

app.post('/orders', async (req, res) => {
  const order = req.body;
  const user = req.user;

  await client.captureSnapshot('order-processing', {
    orderId: order.id,
    total: order.total,
    userId: user.id,
  });

  // Business logic continues...
  res.json({ status: 'ok' });
});
3

Deploy and Verify Traces

Deploy your application and confirm traces are flowing. Check the TraceKit dashboard at /traces to see incoming requests.

# Start your Express app
node server.js

# Verify traces appear in dashboard at /traces
4

Navigate to Code Monitoring

Go to /snapshots and click the "Browse Code" tab. You'll see auto-discovered files and functions from your traces.

5

Set Breakpoints

Breakpoints are auto-registered on the first captureSnapshot call. You can also manually add breakpoints via the UI "Set Breakpoint" button.

6

Trigger a Capture

Make a request that hits a code path with captureSnapshot. The SDK uses new Error().stack for file/line detection and AsyncLocalStorage for request context.

7

View Snapshot

Go to /snapshots and click the captured snapshot. View the captured variables, call stack, request context, security flags, and trace link.

LLM Instrumentation

TraceKit automatically instruments OpenAI and Anthropic SDK calls when detected. LLM calls appear as spans with OTel GenAI semantic convention attributes.

Zero-config auto-instrumentation

If openai or @anthropic-ai/sdk is installed, TraceKit patches them automatically at init. No code changes required.

Captured Attributes

AttributeDescription
gen_ai.systemProvider name (openai, anthropic)
gen_ai.request.modelModel name (gpt-4o, claude-sonnet-4-20250514, etc.)
gen_ai.usage.input_tokensPrompt token count
gen_ai.usage.output_tokensCompletion token count
gen_ai.response.finish_reasonsFinish reason (stop, end_turn, tool_calls)

Configuration

JavaScript
const client = init({
  apiKey: process.env.TRACEKIT_API_KEY,
  serviceName: 'my-service',
  endpoint: 'https://app.tracekit.dev/v1/traces',

  // LLM instrumentation (all enabled by default)
  instrumentLLM: {
    enabled: true,        // Master toggle
    openai: true,         // OpenAI instrumentation
    anthropic: true,      // Anthropic instrumentation
    captureContent: false, // Capture prompts/completions (off by default)
  },
});

Environment Variable Override

Use TRACEKIT_LLM_CAPTURE_CONTENT=true to enable prompt/completion capture without code changes. Useful for enabling in staging but not production.

Streaming Support

Streaming responses produce a single span that covers the entire stream. Token counts are accumulated from the final chunk. No special configuration needed.

LLM Dashboard

View LLM cost, token usage, and latency metrics on the dedicated LLM Observability dashboard at /ai/llm in your TraceKit instance.

Local UI (Development Mode)

Debug Locally Without an Account

The Local UI lets you see traces in real-time on your local machine at http://localhost:9999. Perfect for development - no account signup or API key required!

Quick Start

1. Install the Local UI:

npm install -g @tracekit/local-ui

2. Start the Local UI:

tracekit-local

3. Run your app in development mode:

NODE_ENV=development node app.js

4. Open your browser:

http://localhost:9999

Automatic Detection

The SDK automatically detects when Local UI is running and sends traces to both your local instance and the cloud (if you have an API key configured). No code changes needed!

How It Works

Auto-Detection

SDK checks for Local UI at localhost:9999 on startup

Real-Time Updates

See traces instantly with WebSocket live updates

Development Only

Only activates when NODE_ENV=development

Works Offline

No internet connection required - everything runs locally

Benefits

  • Zero friction onboarding - Try TraceKit without creating an account
  • Faster debugging - No context switching to web dashboard
  • Privacy first - Traces never leave your machine
  • Perfect for demos - Show TraceKit without cloud dependency

Troubleshooting

If traces aren't appearing in Local UI, check:

  • Local UI is running (curl http://localhost:9999/api/health)
  • NODE_ENV=development is set
  • SDK version is v0.3.2 or higher
  • Check console for " Local UI detected" message

Service Discovery

TraceKit automatically instruments outgoing HTTP calls to create service dependency graphs. When your service makes an HTTP request to another service, TraceKit creates CLIENT spans and injects trace context headers.

Supported HTTP Clients

  • http/https - Node.js built-in modules
  • fetch - Node 18+ native fetch API
  • axios - Works via http module
  • node-fetch, got, superagent - Work via http module

Custom Service Name Mappings

For local development or when service names can't be inferred from hostnames, configure service name mappings:

import * as tracekit from '@tracekit/node-apm';

tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY,
  serviceName: 'my-service',
  // Map localhost URLs to actual service names
  serviceNameMappings: {
    'localhost:8082': 'payment-service',
    'localhost:8083': 'user-service',
    'localhost:8084': 'inventory-service',
  },
});

// Now requests to localhost:8082 will show as "payment-service"
const response = await fetch('http://localhost:8082/charge');
// -> Creates CLIENT span with peer.service = "payment-service"

This maps localhost:8082 to "payment-service" in your service graph.

Service Name Detection

TraceKit intelligently extracts service names from URLs:

URLExtracted Service Name
http://payment-service:3000payment-service
http://payment.internalpayment
http://payment.svc.cluster.localpayment
https://api.example.comapi.example.com

Viewing Service Dependencies

Visit your TraceKit dashboard to see the Service Map - a visual graph showing which services call which, with health metrics and latency data.

Manual Instrumentation (Optional)

Express - Manual Spans

import { getClient } from '@tracekit/node-apm';

app.post('/api/process', async (req, res) => {
  const client = getClient();

  const span = client.startSpan('process-data', null, {
    'user.id': req.user?.id,
    'data.size': req.body.items.length,
  });

  try {
    const result = await processData(req.body);

    client.endSpan(span, {
      'result.count': result.length,
    });

    res.json(result);
  } catch (error) {
    client.recordException(span, error as Error);
    client.endSpan(span, {}, 'ERROR');
    throw error;
  }
});

NestJS - Manual Spans

import { Injectable, Inject } from '@nestjs/common';
import { TracekitClient } from '@tracekit/node-apm/nestjs';

@Injectable()
export class DataService {
  constructor(
    @Inject('TRACEKIT_CLIENT') private tracekit: TracekitClient
  ) {}

  async processLargeDataset(data: any[]) {
    const span = this.tracekit.startSpan('process-dataset', null, {
      'dataset.size': data.length,
    });

    try {
      const results = [];
      for (const item of data) {
        const result = await this.processItem(item);
        results.push(result);
      }

      this.tracekit.endSpan(span, {
        'results.count': results.length,
      });

      return results;
    } catch (error) {
      this.tracekit.recordException(span, error as Error);
      this.tracekit.endSpan(span, {}, 'ERROR');
      throw error;
    }
  }
}

TracekitClient Tracing API

The TracekitClient provides these methods for manual span creation and management:

Method Parameters Returns Description
startTrace() op: string — operation name
attrs?: object — initial attributes
Span Creates a new root trace. Use for top-level operations not tied to an incoming HTTP request.
startServerSpan() op: string — operation name
attrs?: object — initial attributes
Span Creates a SERVER span for incoming requests. Extracts trace context from request headers for distributed tracing.
startSpan() op: string — operation name
parent?: Span — parent span (null for auto-link)
attrs?: object — initial attributes
Span Creates a child span. If parent is null, automatically links to the current active span from context.
endSpan() span: Span — the span to end
attrs?: object — final attributes
status?: string — "OK" or "ERROR"
void Ends a span, recording its duration. Optionally sets final attributes and status code.
addEvent() span: Span — target span
name: string — event name
attrs?: object — event attributes
void Records a timestamped event on a span. Use for significant occurrences during an operation (e.g., cache hit, retry).
recordException() span: Span — target span (or null)
error: Error — the exception
void Records an exception on a span with message, type, and stack trace. Automatically sets span status to ERROR.

Lifecycle Methods

Control the SDK lifecycle and query its state:

Method Parameters Returns Description
flush() none Promise<void> Forces an immediate flush of all pending spans to the backend. Call before process exit or after critical operations.
shutdown() none Promise<void> Gracefully shuts down the SDK: flushes remaining data, releases resources, and stops background tasks.
isEnabled() none boolean Returns whether the SDK is currently enabled and actively tracing requests.
shouldSample() none boolean Returns whether the current request should be sampled based on the configured sample rate.
getTracer() none Tracer Returns the underlying OpenTelemetry Tracer instance for advanced use cases requiring direct OTel API access.

Complete Tracing Lifecycle Example

const client = getClient();

// Start a root trace for a background job or non-HTTP operation
const span = client.startTrace('process-order', { 'order.id': orderId });

try {
  // Create a child span for a sub-operation
  const childSpan = client.startSpan('validate-payment', span);
  client.addEvent(childSpan, 'payment-validated', { amount: total });
  client.endSpan(childSpan);

  // End the root span with final attributes
  client.endSpan(span, { 'order.status': 'completed' });
} catch (err) {
  // Record the exception and mark the span as errored
  client.recordException(span, err);
  client.endSpan(span, {}, 'ERROR');
} finally {
  // Ensure all spans are exported before continuing
  await client.flush();
}

Configuration Options

Available configuration options:

tracekit.init({
  // Required: Your TraceKit API key
  apiKey: process.env.TRACEKIT_API_KEY,

  // Optional: Service name (default: 'node-app')
  serviceName: 'my-service',

  // Optional: TraceKit endpoint
  endpoint: 'https://app.tracekit.dev/v1/traces',

  // Optional: Enable/disable tracing (default: true)
  enabled: process.env.NODE_ENV !== 'development',

  // Optional: Sample rate 0.0-1.0 (default: 1.0 = 100%)
  sampleRate: 0.5, // Trace 50% of requests

  // Optional: Enable code monitoring / live debugging (default: false)
  enableCodeMonitoring: true,
});

Environment Variables

Best practice: Store configuration in environment variables:

# .env
TRACEKIT_API_KEY=ctxio_your_generated_api_key_here
TRACEKIT_ENDPOINT=https://app.tracekit.dev/v1/traces
TRACEKIT_SERVICE_NAME=my-nodejs-app
TRACEKIT_CODE_MONITORING_ENABLED=true
NODE_ENV=production

Important Security Note

Never commit your API key to version control. Use environment variables and keep your .env file out of git.

TracekitConfig Interface

The TracekitConfig interface accepts these options when calling init():

Field Type Required Default Description
apiKey string Yes Your TraceKit API key for authenticating with the collector
endpoint string No "app.tracekit.dev" TraceKit collector endpoint URL
tracesPath string No "/v1/traces" HTTP path for trace data export
metricsPath string No "/v1/metrics" HTTP path for metrics data export
serviceName string No "node-app" Name of your service shown in the trace dashboard and service map
enabled boolean No true Enable or disable tracing globally
sampleRate number No 1.0 Trace sampling rate from 0.0 to 1.0 (1.0 = trace 100%)
enableCodeMonitoring boolean No false Enable live code debugging and snapshot capture
autoInstrumentHttpClient boolean No true Auto-instrument outgoing HTTP calls via http/https and fetch
serviceNameMappings Record<string, string> No {} Map host:port patterns to friendly service names for the service map

All configuration options available for the Node.js SDK:

Option Type Default Env Variable Description
apiKey string required TRACEKIT_API_KEY Your TraceKit API key for authentication
endpoint string "app.tracekit.dev" TRACEKIT_ENDPOINT TraceKit collector endpoint URL
tracesPath string "/v1/traces" TRACEKIT_TRACES_PATH HTTP path for trace data export
metricsPath string "/v1/metrics" TRACEKIT_METRICS_PATH HTTP path for metrics data export
serviceName string "node-app" TRACEKIT_SERVICE_NAME Name of your service in the trace dashboard
enabled boolean true TRACEKIT_ENABLED Enable or disable tracing globally
sampleRate number 1.0 TRACEKIT_SAMPLE_RATE Trace sampling rate (0.0 to 1.0, where 1.0 = 100%)
enableCodeMonitoring boolean false TRACEKIT_CODE_MONITORING_ENABLED Enable live code debugging / snapshot capture
autoInstrumentHttpClient boolean true - Auto-instrument outgoing HTTP calls (http/https, fetch)
serviceNameMappings Record<string, string> {} - Map host:port to service names for service discovery

Note: The Node.js SDK does not auto-read environment variables. Read them with process.env and pass to init().

Complete Example

Here's a complete Express + TypeScript example:

import express, { Request, Response } from 'express';
import * as tracekit from '@tracekit/node-apm';

const app = express();
app.use(express.json());

// Initialize TraceKit
tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY!,
  serviceName: 'express-api',
  endpoint: 'https://app.tracekit.dev/v1/traces',
  enabled: process.env.NODE_ENV === 'production',
  enableCodeMonitoring: true, // Optional: Enable live debugging
});

// Add middleware
app.use(tracekit.middleware());

interface User {
  id: string;
  name: string;
  email: string;
}

// Routes - automatically traced!
app.get('/api/users', (req: Request, res: Response) => {
  const users: User[] = [
    { id: '1', name: 'Alice', email: '[email protected]' },
    { id: '2', name: 'Bob', email: '[email protected]' },
  ];
  res.json(users);
});

// Manual span example
app.post('/api/users', async (req: Request, res: Response) => {
  const client = tracekit.getClient();

  const span = client.startSpan('create-user', null, {
    'user.email': req.body.email,
  });

  try {
    // Simulate user creation
    const user: User = {
      id: Date.now().toString(),
      name: req.body.name,
      email: req.body.email,
    };

    // Simulate async operation
    await new Promise(resolve => setTimeout(resolve, 100));

    client.endSpan(span, {
      'user.id': user.id,
    });

    res.status(201).json(user);
  } catch (error) {
    client.recordException(span, error as Error);
    client.endSpan(span, {}, 'ERROR');
    res.status(500).json({ error: 'Failed to create user' });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log('Server running on port ' + PORT);
});

You're all set!

Your Node.js application is now sending traces to TraceKit. Visit the Dashboard to see your traces.

Troubleshooting

Traces not appearing?

Cause: The SDK is not initialized or is initialized after route handlers are registered.

Fix:

  • Call tracekit.init() BEFORE registering any Express/Fastify routes or middleware
  • Verify API key with console.log(process.env.TRACEKIT_API_KEY)
  • Check that enabled is not set to false
  • Look for errors in the console output

Connection refused errors?

Cause: The TraceKit endpoint is unreachable from your Node.js process.

Fix:

  • Test with curl -X POST https://app.tracekit.dev/v1/traces (expect 401)
  • Check proxy/firewall settings
  • Verify the endpoint URL does not have a trailing slash

Code monitoring not working?

Cause: Code monitoring is disabled by default and requires explicit enablement.

Fix:

  • Set enableCodeMonitoring: true in the init options
  • Add captureSnapshot() calls where you want to inspect variables
  • Check the dashboard for active breakpoints
  • Allow up to 30 seconds for the SDK to poll for new breakpoints

Authentication errors (401/403)?

Cause: API key is wrong, has extra whitespace, or was revoked.

Fix:

  • Log the key to verify: console.log(JSON.stringify(apiKey)) (catches invisible characters)
  • Regenerate in dashboard if needed
  • Ensure the correct key is used per environment

Partial or missing spans?

Cause: Auto-instrumentation is not capturing all libraries, or async context is lost.

Fix:

  • Verify autoInstrumentHttpClient: true (default) for outgoing HTTP
  • For database spans, ensure the database library is imported AFTER tracekit.init()
  • Check that async/await is used consistently (callbacks can lose trace context)

Custom Metrics

Track custom metrics like request counts, queue sizes, and response times using the TraceKit metrics API.

Counter

Track monotonically increasing values (requests, events):

import * as tracekit from '@tracekit/node-apm';

const client = tracekit.init({
  apiKey: process.env.TRACEKIT_API_KEY,
  serviceName: 'my-service',
});

// Create a counter with optional tags
const counter = client.counter('http.requests.total', { service: 'api' });

// Increment by 1
counter.inc();

// Add a specific value
counter.add(5);

Gauge

Track values that can go up or down (queue size, connections):

// Create a gauge
const gauge = client.gauge('http.connections.active');

// Set to specific value
gauge.set(42);

// Increment/decrement
gauge.inc();
gauge.dec();

Histogram

Track value distributions (latencies, sizes):

// Create a histogram with tags
const histogram = client.histogram('http.request.duration', { unit: 'ms' });

// Record values
histogram.record(45.2);
histogram.record(123.5);

🔄 Migrating from OpenTelemetry

TraceKit wraps OpenTelemetry internally, so you get the same standards-based trace data with significantly less setup code. Here's how to migrate from a raw OpenTelemetry setup to TraceKit.

Before vs After

Before: Raw OpenTelemetry ~30 lines
// 5 requires
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
    resource: new Resource({
        [SemanticResourceAttributes.SERVICE_NAME]: 'my-service',
    }),
    traceExporter: new OTLPTraceExporter({
        url: 'https://api.tracekit.io/v1/traces',
        headers: { 'Authorization': 'Bearer ' + apiKey },
    }),
    instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
process.on('SIGTERM', () => sdk.shutdown());

// + Express middleware setup
// + manual span creation
After: TraceKit SDK 6 lines
const tracekit = require('tracekit');

tracekit.init({
    apiKey: process.env.TRACEKIT_API_KEY,
    serviceName: 'my-service',
});

// Express middleware: one line
app.use(tracekit.middleware());

Migration Steps

1

Install the SDK: npm install tracekit

2

Replace init code: Remove all @opentelemetry requires and SDK setup. Replace with tracekit.init(). Must be before route imports.

3

Replace middleware: Use app.use(tracekit.middleware()) for Express, or the framework-specific adapter for NestJS

4

Remove OTel packages: npm uninstall @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http @opentelemetry/auto-instrumentations-node

5

Verify: Start your app and check the Traces page for incoming data

Key Migration Benefits

  • 30 lines to 6 lines — no more boilerplate for exporters, resources, instrumentations
  • No OTel dependency management — TraceKit handles version pinning internally
  • Built-in code monitoring — not available with raw OpenTelemetry
  • Built-in security scanning — automatic variable redaction on snapshots
  • Auto-instrumentation included — Express, database, and HTTP calls traced automatically

Performance Overhead

TraceKit is built on OpenTelemetry's efficient batch processing pipeline. The SDK adds minimal overhead to your Node.js application.

Negligible

Request Tracing

< 1ms per request

Spans are batched and exported asynchronously.

Negligible

Code Monitoring (Idle)

Zero overhead

No performance impact when no active breakpoints.

Low

Code Monitoring (Capture)

< 5ms per snapshot

Includes variable serialization and security scanning.

Low

Memory Footprint

~10-20 MB

SDK runtime and span buffer.

Negligible

SDK Initialization

< 200ms one-time

One-time cost at application startup. Does not affect request latency.

Note: Performance characteristics are typical for production workloads and may vary with application complexity, request volume, and number of instrumented libraries. Use sampling configuration to reduce overhead in high-traffic services.

Best Practices

DO: Initialize TraceKit BEFORE requiring routes/modules

The require('tracekit').init() call must be the first line in your entry file so auto-instrumentation patches http, express, and database drivers before they are loaded.

// app.js - TraceKit MUST be first
require('tracekit').init();

const express = require('express');
const app = express();

DO: Use environment variables for API keys

Store your API key in TRACEKIT_API_KEY rather than hardcoding it. The SDK reads this automatically.

DO: Use process.on('SIGTERM') for graceful shutdown

Register a shutdown handler to flush pending spans before the process exits. This is critical in containerized environments where SIGTERM precedes termination.

process.on('SIGTERM', async () => {
  await tracekit.shutdown();
  process.exit(0);
});

DO: Enable code monitoring in staging first

Test breakpoint capture and snapshot behavior in a staging environment before rolling out to production.

DO: Use sampling in high-traffic services

Set TRACEKIT_SAMPLING_RATE to a value below 1.0 for services handling thousands of requests per second to reduce overhead without losing visibility.

DO: Set meaningful service names

Use TRACEKIT_SERVICE_NAME to give your service a descriptive name that makes it easy to identify in the trace viewer.

DON'T: Use dynamic import() for auto-instrumented modules

Auto-instrumentation patches require() calls. If you use ESM import() for modules like express or pg, they may not be instrumented. Use require() for these modules.

DON'T: Create spans for every function

Trace boundaries like HTTP handlers, database calls, and external service calls. Instrumenting internal helper functions adds noise and overhead without useful insight.

DON'T: Add high-cardinality attributes

Avoid using user IDs, request IDs, or session tokens as span attributes. These create excessive unique time series and degrade query performance.

DON'T: Disable TLS in production

The TRACEKIT_INSECURE flag is for local development only. Always use TLS when sending traces to TraceKit in production.

Next Steps

  • Explore your traces on the Traces page to identify performance bottlenecks
  • Set up alert rules to get notified when issues occur
  • Add custom spans for specific operations you want to measure
  • Configure sampling rates for high-traffic applications

Pro Tip

Use the getClient() function to access the TraceKit client anywhere in your application for creating custom spans!

Copilot

No conversations yet

TraceKit Copilot

Ask about services, traces, alerts, snapshots, and LLM usage.

Copilot uses AI to analyze your APM data. Responses may not always be accurate.

Try TraceKit in 10 seconds

Explore real traces, dashboards, and live code monitoring. No signup needed.

Sign Up Free
No credit card No signup Real data