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.
Installation
Section titled “Installation”npm install autotel autotel-adaptersFor HTTP and database auto-instrumentation, also install:
npm install @opentelemetry/auto-instrumentations-node1. Initialise
Section titled “1. Initialise”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, }); }}module.exports = { experimental: { instrumentationHook: true, // not required on Next 15+ },};Edge runtime: for
export const runtime = 'edge'routes, useautotel-edgeinstead.init()from the Node.js package won’t run there.
2. Wrap Route Handlers
Section titled “2. Wrap Route Handlers”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.
Navigation is not an error
Section titled “Navigation is not an error”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.
Patterns
Section titled “Patterns”Server components and server actions
Section titled “Server components and server actions”Use the functional trace() API directly. There’s no request boundary to
wrap:
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 });});Structured errors → toasts
Section titled “Structured errors → toasts”Throw on the server with createStructuredError, parse on the client with
parseError for a richer error UI:
// serverimport { 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', });}// clientimport { parseError } from 'autotel';
try { await fetch('/api/checkout', { method: 'POST', body });} catch (err) { const error = parseError(err); toast.error(error.message, { description: error.why });}Auto-instrumentation
Section titled “Auto-instrumentation”For automatic tracing of fetch, pg, mongodb, and other libraries on the
Node runtime, see
Auto-Instrumentation.
When to use what
Section titled “When to use what”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.
Examples
Section titled “Examples”example-nextjs: Next.js withautotel-adapters/nextandparseError()for client-side error handling.
Related
Section titled “Related”- Adapters: full
autotel-adaptersreference. - Auto-Instrumentation: HTTP, databases, etc.
- Edge Runtimes: for
runtime = 'edge'routes.