Loading…
Loading…
A practical implementation guide for adding OpenTelemetry to Next.js and Node.js apps, including traces, request flow visibility, and production diagnostics.
Tags
In this tutorial, you will instrument a Next.js app and a Node.js service with OpenTelemetry so you can trace requests across both layers, improve debugging, and understand where latency and failures actually happen. The real benefit comes from correlating the frontend-adjacent server layer and backend services, not instrumenting one side in isolation.
Before starting, you should have:
You do not need a large microservices system for this to be useful. Even a Next.js app calling one backend API benefits from shared traces.
The reason to instrument both Next.js and Node.js is to answer end-to-end questions like:
If you only instrument the backend, you miss request-entry context. If you only instrument Next.js, you miss service-level behavior. Together, they become much more useful.
Start with the backend service first. This gives you a stable server-side foundation.
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources \
@opentelemetry/semantic-conventionsThen create the instrumentation bootstrap:
// instrumentation.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
const traceExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
});
export const sdk = new NodeSDK({
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
});And start it before the server boots:
// server.ts
import { sdk } from "./instrumentation";
import { createServer } from "./app";
async function bootstrap() {
await sdk.start();
const app = await createServer();
app.listen(3001);
}
bootstrap();If you run multiple layers, naming discipline matters.
import { Resource } from "@opentelemetry/resources";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "orders-api",
[SemanticResourceAttributes.SERVICE_VERSION]: "1.0.0",
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
process.env.NODE_ENV ?? "development",
});If your services do not identify themselves consistently, trace data becomes hard to navigate very quickly.
Your Next.js application should be treated as its own service boundary, not just a UI wrapper.
Why:
That means tracing Next.js matters for production behavior, not just for framework curiosity.
A useful model is to treat:
Then you can see the full request chain clearly.
The real payoff comes when the trace continues from Next.js into the backend service.
For example:
That makes latency analysis much easier because you can tell:
Auto-instrumentation gives you transport visibility. Manual spans give you domain visibility.
In the backend:
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("orders-api");
export async function processOrder(orderId: string) {
return tracer.startActiveSpan("processOrder", async (span) => {
span.setAttribute("order.id", orderId);
try {
await validateOrder(orderId);
await reserveInventory(orderId);
await capturePayment(orderId);
span.setStatus({ code: 1 });
} catch (error) {
span.recordException(error as Error);
span.setStatus({ code: 2 });
throw error;
} finally {
span.end();
}
});
}In Next.js, useful manual spans often surround:
Traces tell you where time went. Logs tell you what happened.
The strongest setup links them through shared request context:
That turns debugging from "search through logs and guess" into a more structured process.
Metrics should answer operational questions, not just exist because the tooling supports them.
Good starter metrics:
These are enough to surface many real-world problems without drowning the system in noise.
Telemetry pipelines can become accidental data exfiltration paths if you are careless.
Avoid sending:
Observability is valuable, but it must respect privacy and security boundaries.
A useful production setup looks like this:
That gives you one end-to-end debugging path instead of fragmented visibility.
This misses orchestration and latency that occurs in the application layer before the backend service is ever called.
This leaves you blind once the request crosses into the deeper backend path.
Auto-instrumentation is useful, but it does not understand your domain boundaries or key business workflows.
If services, spans, and attributes are inconsistent, the telemetry data becomes much harder to use under pressure.
After the baseline is working, the next improvements usually are:
That is when OpenTelemetry starts becoming part of how the team operates, not just something installed in the repo.
Yes. Next.js apps can be instrumented with OpenTelemetry to trace route handlers, server operations, and request paths, especially when paired with backend services.
Instrumenting both layers helps you understand end-to-end latency, service boundaries, and where failures or slowdowns actually occur.
No. Even mid-sized systems benefit from better request visibility, especially once multiple services, queues, or third-party integrations are involved.
Collaboration
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI: let's bring your vision to life.
Learn how to instrument a Node.js app with OpenTelemetry for traces, metrics, and logs, and build a practical observability setup for production debugging.
A practical guide to building a Backend-for-Frontend with Next.js and Node.js for API aggregation, auth handling, caching, and frontend-specific data shaping.
A practical CI/CD blueprint for Next.js apps using Docker and GitHub Actions, including testing, image builds, deployment stages, cache strategy, and release safety.