This episode makes the tRPC API observable: monitoring through OpenTelemetry, metrics, and logs, tracing requests end-to-end from the client to the server, as well as debugging with the built-in logger and devtools.

A production application cannot be trusted without observability — the ability to understand what is happening inside the system through logs, metrics, and traces. Episode 14 discusses how to apply all three to tRPC: monitoring calls, end-to-end tracing from client to server, and debugging with built-in tooling.
You will learn to measure: how often a procedure is called, how long its execution takes, and at what point a request slows down.
tRPC makes all three easy because every call passes through the middleware pipeline. Grab the procedure metadata with path and type:
const observe = t.middleware(async ({ path, type, ctx, next }) => {
const mulai = Date.now();
const hasil = await next();
const durasi = Date.now() - mulai;
logger.info("trpc.request", {
path,
type,
userId: ctx.userId,
durasiMs: durasi,
ok: hasil.ok,
});
if (typeof metrics?.histogram === "function") {
metrics.histogram("trpc.duration", durasi, { path, type });
}
return hasil;
});The observe middleware records every call to a structured log and also collects a duration histogram per path. One capture point covers the whole API — that is the power of tRPC middleware for observability.
OpenTelemetry provides the standard for traces and metrics. Install the SDK, then connect the tRPC middleware to the global tracer:
npm install @opentelemetry/api @opentelemetry/sdk-node
npm install @opentelemetry/instrumentation-httpimport { trace, SpanStatusCode } from "@opentelemetry/api";
const tracing = t.middleware(async ({ path, next }) => {
const tracer = trace.getTracer("trpc");
return tracer.startActiveSpan(`trpc.${path}`, async (span) => {
try {
const hasil = await next();
span.setStatus({ code: SpanStatusCode.OK });
return hasil;
} catch (err) {
span.setStatus({ code: SpanStatusCode.ERROR });
span.recordException(err as Error);
throw err;
} finally {
span.end();
}
});
});tracer.startActiveSpan creates a span per procedure. Because the span is named from path, you can see the duration of every procedure in Jaeger, Zipkin, or other observability platforms.
Tracing has full value when a single trace connects from the user's click in the browser to the database query. The key is propagating traceparent in the request header:
import { context, trace } from "@opentelemetry/api";
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: "/api/trpc",
headers: () => {
const span = trace.getActiveSpan();
if (!span) return {};
return { traceparent: span.spanContext().traceId };
},
}),
],
});The server reads traceparent in createContext and uses it as the parent span. The result: a single trace showing the full journey — client, HTTP, tRPC middleware, and the database query.
Some metrics you should always monitor:
Build a simple dashboard from these metrics on your favorite platform — starting with a single duration-per-procedure chart is already very helpful.
Since episode 6, loggerLink has been the fastest debugging companion:
import { loggerLink } from "@trpc/client";
loggerLink({
enabled: (opts) =>
process.env.NODE_ENV === "development" || opts.direction === "down",
colorMode: "ansi",
});The configuration above shows full logs in development, and in production only logs incoming responses — enough to see errors without noise. colorMode: "ansi" adds color that makes logs easier to read in the terminal.
For UI debugging, install React Query Devtools. Because tRPC React runs on top of React Query, these devtools display the status of all queries and mutations:
npm install --save-dev @tanstack/react-query-devtoolsimport { ReactQueryDevtools } from "@tanstack/react-query-devtools";
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</trpc.Provider>;Devtools display active queries, cached data, stale times, and buttons to refetch or invalidate manually — the fastest way to understand why a page shows particular data.
Tip
Start observability small: install loggerLink and a duration middleware first. Data gathered over a week will show which endpoints are worth tracing more deeply.
Episode 14 makes your tRPC API transparent: monitoring with structured logs and metrics, end-to-end tracing with OpenTelemetry connecting the client to the server, and quick debugging with loggerLink and React Query Devtools.
Key takeaways:
path and type give per-procedure observability dimensions.path for every call.traceparent in the header for end-to-end tracing.loggerLink and React Query Devtools speed up daily debugging.In the next episode, episode 15, we will discuss resilience & fault tolerance — retry, exponential backoff, and error boundary patterns on the client, data fallback strategies and downtime handling, as well as graceful shutdown for a tRPC server in production.