My Next.js app feels slow in prod—how can I pinpoint which endpoint or database query is causing timeouts?
Application Observability

My Next.js app feels slow in prod—how can I pinpoint which endpoint or database query is causing timeouts?

10 min read

When your Next.js app feels fine locally but crawls in production, guesswork is the slowest thing you can do. You need to see, for each user request, exactly which endpoint, span, or database call is eating your budget until it times out. That’s where tracing with Sentry comes in: you instrument your app once, then trace requests from frontend to backend and through to your database to pinpoint the slow piece of code.

Quick Answer: Use Sentry’s Performance Monitoring and tracing in your Next.js app to capture transactions and spans for each request. Then sort by slowest transactions, inspect the trace, and drill into specific endpoints and database queries causing timeouts.


The Quick Overview

  • What It Is: A Sentry-powered tracing and performance monitoring workflow for Next.js that surfaces slow endpoints and database queries in production.
  • Who It Is For: Next.js teams (frontend, full-stack, and platform engineers) who need code-level visibility into slow requests, timeouts, and backend bottlenecks.
  • Core Problem Solved: You stop guessing which route, API endpoint, or query is “probably” slow and instead see concrete traces, spans, and timing data tied to the exact code that needs to change.

How It Works

At a high level, you add Sentry’s SDK to your Next.js app, enable tracing, and (optionally) instrument your database calls. Sentry then captures each request as a transaction with child spans for framework work, middleware, external calls, and database queries. In the Sentry UI, you sort transactions by duration or p75, drill into traces, and identify the spans responsible for most of the latency.

  1. Instrument your Next.js app with tracing:

    • Install and configure the Sentry SDK for Next.js.
    • Enable browser + server tracing for pages, API routes, and app router handlers.
    • Deploy so real production traffic starts generating performance data.
  2. Capture spans around slow suspects (API + DB):

    • Add Sentry’s HTTP and database integrations (or manual spans) around your calls.
    • Each API hit and DB query is recorded as a span with timing and context.
    • Tie spans to releases so you can see “this got slower after deploy X.”
  3. Use Sentry to hunt down timeouts:

    • In Performance, sort by p75/p95 of transaction duration to find the worst offenders.
    • Open a transaction, follow the trace across services, and find the slow spans.
    • Use filters, tags, and Suspect Spans to isolate exactly which endpoint or query is causing pain.

Features & Benefits Breakdown

Core FeatureWhat It DoesPrimary Benefit
Next.js Performance MonitoringCaptures each request as a transaction with spans for framework, middleware, and handlers.Quickly see which routes/endpoints are slow in production.
Tracing Across Services & DBFollows a request from frontend to backend and into downstream services or databases.Pinpoint where latency is introduced (API vs DB vs external).
Suspect Spans & Performance ViewsSurfaces spans most correlated with slow transactions and regression in performance.Immediately identify the exact function or query to optimize.

Step 1: Instrument your Next.js app with Sentry

This is the “turn the lights on” step. Once it’s done, you get consistent production data instead of one-off logs from that one user who complained.

Install the Next.js SDK

In your Next.js project:

npm install --save @sentry/nextjs
# or
yarn add @sentry/nextjs

Initialize Sentry (for the modern app router, pages router, or both). For a basic setup, you’ll typically have:

  • sentry.client.config.ts / sentry.client.config.js
  • sentry.server.config.ts / sentry.server.config.js

Example (server config, simplified):

// sentry.server.config.ts
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.2,    // start small, adjust based on traffic
  environment: process.env.NODE_ENV || "production",
});

And client config:

// sentry.client.config.ts
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.2,
});

Key detail: tracesSampleRate controls what fraction of transactions you capture. Start with something conservative (e.g., 0.1–0.3), then adjust once you see your volume.

Deploy and verify

  • Deploy your Next.js app as usual (Vercel, self-hosted, etc.).
  • Trigger a few requests in production.
  • In Sentry, go to Performance → Transactions and confirm you see data for your routes.

If you don’t see transactions, check:

  • DSN is set and available to both server and client.
  • Sentry files are being imported by Next.js (they usually are when placed in the root using the Next.js SDK pattern).
  • tracesSampleRate isn’t set to 0.

Step 2: Capture spans for API endpoints and database queries

Once basic tracing is on, you want more granularity inside your slow transactions.

Instrument Next.js API routes / route handlers

For pages/api routes:

// pages/api/users.ts
import { withSentry } from "@sentry/nextjs";

async function handler(req, res) {
  // Your logic here
}

export default withSentry(handler);

For the app router (app/api), you typically rely on the automatic integration, but you can still add manual spans when needed.

Sentry now records these API requests as part of a trace, with spans showing framework and handler execution time.

Add spans around database calls

If you’re using an ORM or DB library, you have two options:

  1. Use an existing integration (where available).
  2. Add manual spans around your database calls.

Manual span example:

import * as Sentry from "@sentry/nextjs";
import { db } from "@/lib/db";

export async function GET() {
  const transaction = Sentry.getCurrentHub().getScope()?.getTransaction();

  const span = transaction?.startChild({
    op: "db.query",
    description: "SELECT * FROM users WHERE is_active = true",
  });

  const users = await db.user.findMany({ where: { isActive: true } });

  span?.finish();

  return new Response(JSON.stringify(users));
}

Now, when that endpoint is slow, you’ll see a db.query span with its duration inside the transaction trace. If that span dominates the request, you’ve found your bottleneck.

Track external services (upstream APIs, queues, etc.)

Wrap outbound calls too:

const span = Sentry.getCurrentHub()
  .getScope()
  ?.getTransaction()
  ?.startChild({
    op: "http.client",
    description: "GET https://payments.example.com/charge",
  });

const res = await fetch("https://payments.example.com/charge", { method: "POST", body: payload });

span?.finish();

This helps you distinguish between “our app is slow” and “our dependency is slow.”


Step 3: Use Sentry to hunt slow endpoints and queries

Once traces and spans are flowing, you get to the part you actually care about: which endpoint or database query is causing timeouts?

Find your slowest requests

In Sentry:

  1. Go to Performance → Transactions.
  2. Set the environment to production.
  3. Sort by p75 or p95 Duration.
  4. Filter on operation or name (e.g., transaction:/api/users).

This surfaces the endpoints that are consistently slow for a large chunk of users, not just single outliers.

Typical patterns you’ll see:

  • A few GET /api/* endpoints with much higher p95 than everything else.
  • Very slow POST routes tied to complex writes, email sends, or third-party calls.
  • Slow page transitions for specific dynamic routes.

Inspect traces and spans

Click into a slow transaction to see the full trace:

  • Timeline view: Spans aligned by time, so you see what’s blocking.
  • Span details: Duration, op, description, and tags (e.g., DB table, HTTP URL, etc.).
  • Distributed trace: If the request hops from frontend → gateway → API service, you can follow it end-to-end.

Look for:

  • A single db.query span taking 1–2 seconds.
  • Multiple similar spans indicating N+1 queries.
  • Long http.client spans to internal or external services.
  • Gaps that indicate blocking I/O or heavy CPU (profiling can help here).

This is how you move from “/api/users is slow” to “the SELECT * FROM users query with no index is slow.”

Use Suspect Spans and Performance views

Sentry’s performance views and Suspect Spans help you automate the “needle in a haystack” part:

  • Suspect Spans: highlight spans most correlated with slow transactions.
  • Trends / Regressions: show endpoints that recently got slower after a deploy.

Workflow:

  1. From Performance, open a transaction and scroll to Suspect Spans.
  2. Identify spans that consistently appear in the slowest transactions.
  3. Use that span’s description (like the query or endpoint name) to find the corresponding code.

Now you know which function, endpoint, or query to optimize—and which commit likely introduced the regression.


Features & Benefits Breakdown (Recap)

Core FeatureWhat It DoesPrimary Benefit
Next.js SDK with TracingAutomatically captures transactions for page loads, navigations, and API routes.Immediate visibility into which Next.js endpoints are slow in prod.
Manual Spans + DB InstrumentationWraps your database queries and external calls in spans with timing and context.Shows exactly which query or external call is causing timeouts.
Performance Views & Suspect SpansSurfaces the worst-performing endpoints and spans and how they changed over time.Faster root cause analysis and targeted optimization work.

Ideal Use Cases

  • Best for debugging a “slow in prod but fine locally” Next.js app: Because Sentry shows you real transaction and span timing on production hardware, under real user traffic, instead of relying on local benchmarks.
  • Best for tracking performance regressions after releases: Because Sentry ties transactions and spans to releases and commits, so you can see exactly which deploy made /api/orders go from 300ms to 2s.

Limitations & Considerations

  • Sampling and volume limits: Tracing can generate a lot of data. Use tracesSampleRate or dynamic sampling to keep volume aligned with your Sentry quotas and still capture a representative spread of traffic.
  • Instrumentation completeness: If you don’t wrap your database and external calls with spans (or use integrations), you’ll still see slow endpoints, but won’t always know why. Aim to cover your critical code paths.

Pricing & Plans

Sentry’s performance and tracing capabilities are available across plans, with quotas for transactions and other event types. You can:

  • Start on a free or lower-tier plan, then scale up as you send more events.
  • Set quotas and add pay-as-you-go budget to handle traffic spikes without surprise outages in monitoring.
  • Reserve higher volumes ahead of time for discounts when your usage grows.

Typical pattern for teams chasing slow Next.js endpoints:

  • Developer / Team plan: Best for small teams or new apps needing visibility into a handful of core endpoints and slow queries, with basic dashboards and alerts.
  • Business+ / Enterprise: Best for larger orgs with many services and teams, needing SAML + SCIM, organization-wide governance, higher volume, and Enterprise support.

For up-to-date pricing details, check Sentry’s pricing page when you sign up or talk to Sales if you’re planning heavier usage.


Frequently Asked Questions

Do I need to instrument every single database query to find slow ones?

Short Answer: No, but you should instrument the critical paths you care about.

Details: Start with the endpoints that directly affect user experience—like login, checkout, dashboards, and key API routes. Wrap the main DB queries in spans or use an integration that does this for you. Once you see which endpoints are slow in Sentry, you can iteratively add more spans around suspicious parts of the code. You don’t need 100% coverage on day one; you need enough to explain your worst transactions.


Will tracing slow down my Next.js app or increase my timeout problems?

Short Answer: Tracing does add overhead, but it’s designed to be minimal and controlled via sampling.

Details: Sentry’s SDK captures spans and sends them asynchronously, so it doesn’t block your response path. There is some overhead (as with any instrumentation), but in practice it’s small compared to the latency you’re trying to eliminate. You can tune tracesSampleRate or use dynamic sampling to reduce volume if you’re concerned. If your app is hitting timeouts now, the tracing overhead is almost never the root cause—you’ll usually find slow queries, chatty microservice calls, or CPU-heavy logic instead.


Summary

When your Next.js app feels slow in production, “it’s probably the database” isn’t a debugging strategy—it’s a shrug. By wiring Sentry’s Next.js SDK with tracing and spans, you turn every slow request into an inspectable transaction: you see which endpoint is affected, how long each span took, which database query or external call is actually blocking, and which release introduced the regression. That’s how you move from generic performance complaints to concrete code changes that actually fix timeouts.


Next Step

Get Started