Skip to content

Next.js

Use autotel-adapters/next to wrap App Router Route Handlers with one helper. It creates a span per request and exposes a request-scoped logger. Pair with init() from autotel for the SDK setup.

Terminal window
npm install autotel autotel-adapters

For HTTP and database auto-instrumentation, also install:

Terminal window
npm install @opentelemetry/auto-instrumentations-node

Use Next.js’s official instrumentation hook so init() runs before any request is served:

// instrumentation.ts (project root)
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { init } = await import('autotel');
init({
service: 'my-next-app',
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
});
}
}
next.config.js
module.exports = {
experimental: {
instrumentationHook: true, // not required on Next 15+
},
};

Edge runtime: for export const runtime = 'edge' routes, use autotel-edge instead. init() from the Node.js package won’t run there.

app/api/checkout/route.ts
import { withAutotel, useLogger, parseError } from 'autotel-adapters/next';
export const POST = withAutotel(async (request: Request) => {
const log = useLogger(request);
log.set({ feature: 'checkout' });
try {
const body = await request.json();
const result = await processCheckout(body);
log.set({ orderId: result.id });
return Response.json(result);
} catch (error) {
const parsed = parseError(error);
log.set({ error_status: parsed.status, error_why: parsed.why });
throw error;
}
});

withAutotel creates the span and the request logger; useLogger(request) returns the logger bound to the active request.

redirect() and notFound() work by throwing. Next catches the throw and turns it into a response, so the throw is control flow rather than a failure.

withAutotel asks Next itself to classify each throw, then rethrows navigation signals untouched. A redirect leaves no phantom error on the span and no error in your dashboards:

export const GET = withAutotel(async (request: Request) => {
const session = await getSession(request);
if (!session) redirect('/login'); // span stays ok
if (!session.team) notFound(); // span stays ok
throw new Error('boom'); // this one is recorded
});

Delegating to Next rather than matching on error messages means a signal wrapped in cause still reads as navigation, and new signal types stay covered without an autotel release.

Use the functional trace() API directly. There’s no request boundary to wrap:

app/orders/page.tsx
import { trace, withTracing } from 'autotel';
const loadOrders = withTracing({})((ctx) => async (userId: string) => {
ctx.setAttribute('user.id', userId);
return db.orders.findMany({ where: { userId } });
});
export default async function Page() {
const orders = await loadOrders('123');
return <OrdersTable orders={orders} />;
}
// app/actions.ts ("use server")
'use server';
import { trace, withTracing } from 'autotel';
export const createOrder = withTracing({})((ctx) => async (data: OrderData) => {
ctx.setAttribute('source', 'server-action');
return db.orders.create({ data });
});

Throw on the server with createStructuredError, parse on the client with parseError for a richer error UI:

// server
import { createStructuredError } from 'autotel';
if (!user) {
throw createStructuredError({
message: 'User not found',
status: 404,
why: `No user with ID "${userId}"`,
fix: 'Check the URL and try again',
});
}
// client
import { parseError } from 'autotel';
try {
await fetch('/api/checkout', { method: 'POST', body });
} catch (err) {
const error = parseError(err);
toast.error(error.message, { description: error.why });
}

For automatic tracing of fetch, pg, mongodb, and other libraries on the Node runtime, see Auto-Instrumentation.

Use withAutotel() for Route Handlers and Pages Router API routes (pages/api/*). Use useLogger() on its own in server components or actions where a span already exists higher up the call stack; wrapping with withAutotel() again would duplicate the span. For domain functions and server actions outside an HTTP boundary, use trace() from autotel directly.

  • example-nextjs: Next.js with autotel-adapters/next and parseError() for client-side error handling.