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/nexusTelescope is the Node-only entry point @inverge/nexus/telescope; browser
bundles never include it.
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 W3Ctraceparentso the services you call join the trace. consoleoutput as log entries.pg,mysql2andioredisqueries 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()); // lastJobs, 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
| Option | Default | |
|---|---|---|
service | NEXUS_SERVICE_NAME, else the package name | Name in traces |
release | — | |
sampleRate | null (console) | Failed, slow and monitored journeys are always kept |
ignorePaths, ignoreTypes | — | Merged with the console's |
redactKeys | — | Added to the defaults |
captureBodies / bodySizeLimit | true / 64 KB | |
maxEntries | 2000 | Per journey |
flushIntervalMs / timeoutMs | 2000 / 3000 | Delivery cadence and timeout |
enabled | true | Also NEXUS_TELESCOPE_ENABLED=false |
onError | silent | Called on delivery failures |
Journeys are flushed on shutdown; await telescope.flush() forces it.
