Aviary

Telescope

Laravel Telescope, redesigned for NestJS — a headless observability console that correlates every request, query, job, mail, cache hit, and exception under one batch, with a pluggable store and an optional dashboard.

@dudousxd/nestjs-telescope is an application observability console for NestJS. A set of watchers capture what happens inside a request — every query, queued job, sent email, cache hit, and thrown exception — correlate it all under one batch via AsyncLocalStorage, persist it through a pluggable storage provider, and expose it through a headless JSON API plus an optional dashboard. It's the missing Telescope for NestJS, designed framework-idiomatically rather than ported.

The unit of value is the batch

The thing you click into is never a lone log line — it's one request and everything it caused. Open an entry and you see the exact queries, jobs, mails, and exceptions it produced, in capture order, on a single waterfall.

The problem it solves

Backend developers reach for Telescope in two moments. Locally, debugging "why did this request do 47 queries?" — the answer lives across a dozen scattered log lines that you have to mentally stitch back together. In production, where an admin needs to inspect a failing flow without SSHing into a pod to grep logs after the fact.

Telescope answers both by recording correlated entries you click into, not time-series aggregates. You install core plus one adapter, add one module import, and within five minutes you're stepping through a request — in dev with full detail, in prod with redaction always on and a closed-by-default gate.

It complements metrics and tracing; it doesn't replace them. Metrics tell you that p95 latency rose. Telescope tells you which request, and shows you the SQL that did it.

How it's built

Four design choices define the library:

  • Watchers, correlated under one batch. Each watcher (request, query, job, mail, cache, schedule, HTTP-client, exception) records an entry; AsyncLocalStorage ties them to the request that triggered them. See Capture & correlation.
  • Off the response path. Request capture fires after the response is flushed and query capture costs microseconds, so Telescope never slows the app it observes — and it surfaces its own host-path cost on a /health card. See Performance.
  • Pluggable storage. Every store implements one StorageProvider SPI with a self-healing schema: zero-config SQLite by default, swap in Redis for multi-instance, or write your own. See Storage.
  • Headless API, optional dashboard. Everything the UI shows comes from a plain JSON API you can curl, script, or build your own admin against. The bundled React dashboard is a separate module you opt into — no frontend dependency imposed on your app.

Quickstart

The whole loop — install, register two modules, open the dashboard — in three steps. For prerequisites, the headless API, storage adapters, and watchers, see Getting Started.

Install core and the dashboard:

pnpm add @dudousxd/nestjs-telescope @dudousxd/nestjs-telescope-ui

Register both modules in your root module. TelescopeModule.forRoot() wires request and exception capture and turns on the zero-config SQLite store and pruner; TelescopeUiModule.forRoot() serves the dashboard SPA:

app.module.ts
import { Module } from '@nestjs/common';
import { TelescopeModule } from '@dudousxd/nestjs-telescope';
import { TelescopeUiModule } from '@dudousxd/nestjs-telescope-ui';

@Module({
  imports: [
    TelescopeModule.forRoot({
      // dev: rich capture, open gate. prod: flip enabled + supply an authorizer.
      enabled: process.env.NODE_ENV !== 'production',
      authorizer: () => true, // gates the API; defaults to deny in production
      prune: { after: '24h' },
    }),
    TelescopeUiModule.forRoot(),
  ],
})
export class AppModule {}

Boot the app and open http://localhost:3000/telescope. The dashboard polls /telescope/api/*, shows captured entries with live polling, and — on any entry — the correlated batch: the request and everything it caused. Prefer JSON? Hit GET /telescope/api/entries/:id for the same data.

Production gate

The API gate denies in production by default. In prod you must set enabled: true and supply an authorizer (or wire dashboard auth) — otherwise the dashboard returns 403. This is a default, not an afterthought: capture runs with redaction always on, and the gate stays closed until you open it.

Beyond capture

Once entries are flowing, the rest of the console builds on the same pipeline:

  • A Horizon-style queue console — browse BullMQ and SQS queues in real time, and (behind a second default-deny gate) retry / remove / promote / redrive jobs.
  • A Pulse-style health dashboard — per-type counts, slowest entries, top exceptions, N+1 hotspots, and a throughput sparkline, rolled up from captured entries.
  • Error alerting — a genuinely new exception family pages you via Slack (Block Kit, route/user, deep link), a webhook, or your own sink, plus rate and slow-route rules.
  • AI diagnosis@dudousxd/nestjs-telescope-ai turns an exception into a probable-cause / suggested-fix report (Bedrock, OpenAI, any Vercel AI SDK model).
  • MCP for coding agents — an optional Model Context Protocol server lets Claude Code, Cursor, or any MCP client debug straight from the captured data.
  • Archive before prune — per-type retention plus an archive.sink ships doomed entries to S3 before the pruner deletes them.

Where to go next

On this page