InvergeDocs
Telescope

Telescope for Node.js

One init() call records every incoming request for Express, Fastify, Koa, NestJS, Hapi and Next.js — plus outgoing calls, queries, logs and jobs.

Install

npm install @inverge/nexus

Telescope is the Node-only entry point @inverge/nexus/telescope; browser bundles never include it.

instrument.ts — import this first
import { NexusTelescope } from '@inverge/nexus/telescope';

export const telescope = NexusTelescope.init({
  apiKey: process.env.NEXUS_API_KEY!,
  service: 'orders-api',
});

Call init() before your app creates its server (import it first, or use node --import ./instrument.js).

What init() records

  • Every incoming request — it hooks http.Server, so Express, Fastify, Koa, NestJS, Hapi and Next.js all work without framework code.
  • Outgoing HTTP — fetch (undici), http / https, and everything built on them (axios, got…), with a W3C traceparent so the services you call join the trace.
  • console output as log entries.
  • pg, mysql2 and ioredis queries and commands.

Turn any of it off:

NexusTelescope.init({
  apiKey,
  instrument: { console: false, ioredis: false },   // server · client · console · pg · mysql2 · ioredis
});

Framework helpers

They add the matched route (so /orders/:id groups as one endpoint), the parsed body, and exceptions the framework would otherwise swallow.

app.use(telescope.express());        // first
// … routes …
app.use(telescope.expressErrors());  // last

Jobs, commands and cron

// Producer — inside a request: put the trace on the job
await queue.add('send-invoice', { orderId, ...telescope.inject('SendInvoice', 'emails') });

// Worker (BullMQ shown) — the job becomes its own journey in the same trace
new Worker('emails', (job) =>
  telescope.job(job.name, () => sendInvoice(job.data), { carrier: job.data, queue: 'emails' }),
);

await telescope.command('reports:daily', runReport);
cron.schedule('0 * * * *', () => telescope.schedule('hourly report', runReport, '0 * * * *'));

Your own entries

const quote = await telescope.span('price lookup', () => pricing.quote(order)); // nests across await
telescope.query('select …', [id], 1.2, 'clickhouse');
telescope.event('OrderPriced', quote);
telescope.cache('hit', `price:${id}`);
telescope.log('warning', 'retrying gateway', { attempt: 2 });
telescope.exception(err, true);
telescope.user({ id: user.id, email: user.email });   // tags Auth:<id>, links the session
telescope.tag(['vip']);

Context is kept with AsyncLocalStorage, so concurrent requests never mix.

Options

OptionDefault
serviceNEXUS_SERVICE_NAME, else the package nameName in traces
release—
sampleRatenull (console)Failed, slow and monitored journeys are always kept
ignorePaths, ignoreTypes—Merged with the console's
redactKeys—Added to the defaults
captureBodies / bodySizeLimittrue / 64 KB
maxEntries2000Per journey
flushIntervalMs / timeoutMs2000 / 3000Delivery cadence and timeout
enabledtrueAlso NEXUS_TELESCOPE_ENABLED=false
onErrorsilentCalled on delivery failures

Journeys are flushed on shutdown; await telescope.flush() forces it.

On this page