How do I enable Sentry performance monitoring (tracing) for a Node.js API and set sampling so costs don’t blow up?
Application Observability

How do I enable Sentry performance monitoring (tracing) for a Node.js API and set sampling so costs don’t blow up?

11 min read

Most Node.js teams want tracing in production but don’t want a surprise bill when traffic spikes. You can absolutely have both: detailed Sentry performance monitoring for your Node.js API and a sane sampling strategy that keeps span volume (and cost) under control.

In this walkthrough, I’ll show you how I typically instrument a Node.js API with Sentry tracing, then dial in sampling so you get useful coverage without flooding your quotas.

Quick Answer: Install and initialize the Sentry Node SDK with tracing integrations (like Express), enable tracesSampleRate or tracesSampler for performance monitoring, and use environment-aware, rules-based sampling so you capture 100% of what matters (like errors and critical endpoints) and aggressively sample everything else.


The Quick Overview

  • What It Is: Sentry performance monitoring for Node.js is transaction and span tracing that shows where time is spent across your API handlers, downstream calls, and services. It’s built into the Sentry Node SDK—same place you get error monitoring.
  • Who It Is For: Backend and full-stack developers running Node.js APIs (Express, Fastify, NestJS, raw HTTP) who want to see slow endpoints, N+1 queries, and bad deploys before users start filing tickets.
  • Core Problem Solved: When latency jumps in production, you usually know “something is slow” but not which route, which query, or which release caused it. Sentry tracing turns those production slowdowns into code-level issues and transactions you can actually debug.

How It Works

At a high level, Sentry tracing for Node.js works like this:

  1. The Sentry SDK wraps your HTTP server / framework (Express, Fastify, etc.) to create transactions for incoming requests.
  2. As your code runs, Sentry instruments spans for operations like database queries, external HTTP calls, and custom code blocks.
  3. These transactions and spans are sampled according to your tracesSampleRate or tracesSampler logic, then sent to Sentry, where they’re grouped, visualized, and correlated with:
    • Errors/exceptions
    • Releases and deploys
    • Session Replay (if enabled in the frontend)
    • Logs and Profiling (where available)

From there, you can:

  • Use Performance views and Discover/Insights to query slow transactions.
  • Set alerts so you’re notified when latency or throughput crosses a threshold.
  • Use Ownership Rules and Suspect Commits so the right team gets the issue with the right context.

Step 1: Install the Sentry Node SDK with Tracing

For a typical Node.js API using Express:

npm install @sentry/node @sentry/tracing
# or
yarn add @sentry/node @sentry/tracing

Note: In newer SDK versions, tracing is folded into @sentry/node, but including @sentry/tracing is still common in many codebases. Check the current Sentry Node docs if you’re starting fresh.


Step 2: Initialize Sentry in Your Node.js API

Here’s a minimal Express example with performance monitoring enabled:

// sentry.js or at the very top of your server entry file
const Sentry = require('@sentry/node');
const Tracing = require('@sentry/tracing');

Sentry.init({
  dsn: process.env.SENTRY_DSN,

  // Enable performance monitoring by setting a sample rate
  // Start with something low in production (e.g. 0.1 or lower)
  tracesSampleRate: 0.1,

  // Optional: Tag the environment to separate data by env in Sentry
  environment: process.env.NODE_ENV || 'development',

  // Optional but recommended: associate transactions with releases
  release: process.env.SENTRY_RELEASE,
});

And wire it into Express:

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

// Request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());

// Tracing handler creates a transaction for every incoming request
app.use(Sentry.Handlers.tracingHandler());

app.get('/health', (req, res) => {
  res.send('ok');
});

// Your routes...

// The error handler should be before any other error middleware
app.use(Sentry.Handlers.errorHandler());

// Your own error handler (if any) should come after Sentry’s
app.use((err, req, res, next) => {
  res.status(500).send('Something broke');
});

app.listen(process.env.PORT || 3000, () => {
  console.log('Server started');
});

With just this, Sentry will start creating transactions for incoming requests and send them according to tracesSampleRate.


Step 3: Use Framework Integrations for Better Traces

If you’re using a framework like Express or Fastify, make sure you enable the integration explicitly when needed:

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  integrations: [
    // Automatically instrument Node.js HTTP and Express
    new Sentry.Integrations.Http({ tracing: true }),
    new Tracing.Integrations.Express({ app }),
  ],
  tracesSampleRate: 0.1,
});

For other servers (Fastify, Koa, NestJS, etc.), there are similar integrations or manual hooks. The pattern is the same:

  • Initialize Sentry early.
  • Attach a request handler (or plugin).
  • Enable HTTP + framework tracing integration.

Step 4: Add Custom Spans Around Important Code

Automatic instrumentation is a good starting point, but the best traces are the ones that match your mental model of the system.

For example, wrap critical code paths:

app.get('/orders/:id', async (req, res, next) => {
  const transaction = Sentry.getCurrentHub().getScope().getTransaction();

  const span = transaction?.startChild({
    op: 'business_logic',
    description: 'Fetch order details',
  });

  try {
    const order = await fetchOrderFromDb(req.params.id);
    // maybe another span around payment provider call, etc.
    res.json(order);
  } catch (err) {
    next(err);
  } finally {
    span?.finish();
  }
});

These spans show up inside the request transaction, so when you click into a slow /orders/:id route in Sentry, you’ll see exactly which part of the code was slow.


Step 5: Control Costs with Sampling

This is where you decide how much performance data you want Sentry to ingest.

You have two knobs:

  1. tracesSampleRate — a single static probability (0–1) applied to all transactions.
  2. tracesSampler — a function that receives transaction context and returns a sample rate per transaction.

Option A: Simple Static Sampling (tracesSampleRate)

This is the easiest way to start:

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.05 : 1.0,
});
  • 1.0 means “capture 100% of transactions.”
  • 0.05 means “capture 5% of transactions.”
  • Good for getting started and rough visibility.
  • Not great if you want to treat paths or environments differently.

Option B: Rules-Based Sampling (tracesSampler)

For more control, use tracesSampler. This lets you, for example:

  • Capture 100% of erroring transactions.
  • Capture 50% of transactions for a critical endpoint like /checkout.
  • Capture 1% of everything else in production.
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampler: (samplingContext) => {
    const { transactionContext, parentSampled } = samplingContext;
    const name = transactionContext.name || '';

    // If there's a parent trace decision, respect it (keeps distributed traces coherent)
    if (typeof parentSampled === 'boolean') {
      return parentSampled ? 1.0 : 0.0;
    }

    // Always keep transactions from non-production for debugging
    if (process.env.NODE_ENV !== 'production') {
      return 1.0;
    }

    // Keep all transactions for checkout flow
    if (name.includes('/checkout')) {
      return 1.0;
    }

    // Sample more aggressively for noisy endpoints like health checks
    if (name.includes('/health')) {
      return 0.01;
    }

    // Default production rate
    return 0.02; // 2%
  },
});

This is generally what I recommend for production APIs: treat your high-value flows and environments differently.


A Practical Sampling Strategy That Won’t Blow Up Costs

If you want a concrete starting point, here’s a sanity-checked setup I’d use on a production Node.js API:

  • Development / Staging

    • tracesSampleRate: 1.0
    • Goal: Capture everything. This is where you debug instrumentations and verify traces.
  • Production (Initial)

    • Start with tracesSampleRate: 0.02 (2%) or a tracesSampler that:
      • 100% for /checkout, /login, /signup, and any “money” endpoints.
      • 0.1–1% for health checks, metrics endpoints, or bot-heavy routes.
      • 2–5% for everything else.
  • Production (After a Week)

    • Look at transaction volume in Sentry. If you’re far below your quotas, gradually raise the default rate.
    • If you’re approaching your quotas, lower the default or tighten rules.
    • Use Sentry’s quotas and usage charts to stay ahead of spikes.

Sampling is probabilistic, so don’t obsess over exact numbers on day one. Start conservative, check actual event counts in Sentry, and adjust.


Features & Benefits Breakdown

Core FeatureWhat It DoesPrimary Benefit
Transaction & Span TracingCaptures Node.js request lifecycles and operations (DB, HTTP, custom spans)Shows exactly where time is spent in API calls, down to specific spans and code paths
Environment-Aware SamplingLets you set different sample rates per env or per routeKeeps production costs predictable while giving full visibility in dev and on critical endpoints
Release & Ownership IntegrationTies traces to releases, deploys, and code ownersHelps you see when a deploy introduced a slowdown and routes issues to the team that owns the code

Ideal Use Cases

  • Best for Node.js APIs with uneven traffic patterns: Because it lets you prioritize critical routes (checkout, auth) and heavily sample low-value noise (health checks, bots), you get signal without burning through quotas.
  • Best for teams connecting frontend → backend traces: Because Sentry can connect browser transactions to backend Node.js spans, you can trace through services and identify which service actually made the user’s page slow.

Limitations & Considerations

  • Sampling doesn’t retroactively recover data: If you set tracesSampleRate to 0.01, you’ll only ever see 1% of transactions. For rare but critical paths, explicitly bump their sample rate in tracesSampler.
  • Very high throughput systems still need guardrails: Even at 1–2%, a massive API can generate a lot of spans. Use Sentry’s quotas and monitoring to keep an eye on event volume and adjust sampling as your traffic grows.

Pricing & Plans

You can get started with Sentry performance monitoring for Node.js on the free plan, which includes a baseline of transactions. Pricing then scales by:

  • Events (errors/exceptions)
  • Transactions (traces)
  • Attachments and monitors (if you use those features)

Key points:

  • You can set quotas for transactions so you don’t exceed your budget.
  • You can add pay-as-you-go budget for overages when you need extra headroom.
  • You can reserve volume ahead of time for discounts if you know your traffic patterns.

Common pattern for Node.js teams:

  • Start on Developer or Team plans.

  • Set conservative tracesSampleRate and quotas.

  • Increase reserved transaction volume once you see the value from traces and have baseline usage numbers.

  • Developer Plan: Best for small teams or individual developers needing code-level visibility into errors and performance for a few services.

  • Team / Business Plans: Best for growing teams needing more transactions, multiple projects, dashboard limits, SAML/SCIM (Business+), and tighter governance.

Check current details and calculators on the Sentry pricing page.


Frequently Asked Questions

Do I need to enable both error monitoring and performance monitoring in Node.js?

Short Answer: No extra product switch—performance monitoring is part of the same Node SDK; you enable it by setting tracesSampleRate or tracesSampler.

Details:
When you call Sentry.init in your Node.js API, error monitoring is enabled by default: uncaught exceptions and unhandled rejections get captured as error events. Performance monitoring (transactions and spans) only starts once you:

  1. Add the tracing integration (like Tracing.Integrations.Express or HTTP integration).
  2. Set tracesSampleRate or tracesSampler.

So you can:

  • Run errors-only by omitting these tracing options.
  • Or enable both errors + tracing and use sampling to keep volumes under control.

How do I know if my tracing sample rate is too high or too low?

Short Answer: Watch your transaction volume compared to quotas and see if you’re getting enough coverage to debug issues; adjust up or down based on real usage.

Details:
In practice:

  1. Start low in production (1–5%) and 100% in dev/staging.
  2. In Sentry:
    • Look at UsagePerformance / Transactions to see how many traces you’re ingesting.
    • Check if you’re hitting or nearing quotas.
  3. Check Performance views:
    • If important endpoints have very few samples over hours/days, increase sample rate for those endpoints specifically via tracesSampler.
    • If you’re getting plenty of data but approaching your quota, lower the global default rate.
  4. Revisit sampling rules after:
    • Traffic spikes or new features.
    • Plan changes or reserved volume updates.

The nice part: sampling is all config, so you can tune it iteratively as you learn how much data you actually need to debug comfortably.


Summary

Enabling Sentry performance monitoring for a Node.js API comes down to a few concrete steps:

  1. Install @sentry/node (and @sentry/tracing where applicable).
  2. Initialize Sentry early in your server with:
    • DSN
    • Framework integrations (Express, HTTP, etc.)
    • tracesSampleRate or tracesSampler
  3. Wrap your API with requestHandler and tracingHandler middleware.
  4. Add custom spans around key business logic.
  5. Use environment- and route-aware sampling to:
    • Capture 100% of what’s critical.
    • Aggressively sample what’s noisy.
    • Stay comfortably within your transaction quotas.

You end up with exactly what most teams want: a clear, code-level view of where your Node.js API is slow and broken, without lighting up your budget.


Next Step

Get Started