@dudousxd/nestjs-telescope (core)
The core module — request + exception watchers, the recorder, ALS correlation, the zero-config SQLite store, the headless API, the gate, and the pruner.
pnpm add @dudousxd/nestjs-telescopenpm install @dudousxd/nestjs-telescopeCore is everything you need to start capturing: it wires request and exception watchers automatically, turns on a zero-config SQLite store and pruner, and exposes the headless API. Import the module and you're recording — no adapter required.
import { TelescopeModule } from '@dudousxd/nestjs-telescope';
@Module({
imports: [
TelescopeModule.forRoot({
enabled: process.env.NODE_ENV !== 'production',
authorizer: () => true, // gates the API; defaults to deny in production
prune: { after: '24h' },
}),
],
})
export class AppModule {}Hit GET /telescope/api/entries (or /telescope/api/entries/:id for a request and everything it caused).
Built-in watchers
| Export | Captures |
|---|---|
| request watcher | Every HTTP request (wired automatically) — opens the batch. |
| exception watcher | Every thrown exception (wired automatically). |
HttpClientWatcher | Outbound fetch calls, correlated to the request/job. Optionally also axios / @nestjs/axios. Sanitizes credentials and secret query params; no peer dependency. |
import { TelescopeModule, HttpClientWatcher } from '@dudousxd/nestjs-telescope';
TelescopeModule.forRoot({ watchers: [new HttpClientWatcher({ slowMs: 1000 })] });HttpClientWatcher + axios / @nestjs/axios
fetch is patched out of the box, but NestJS apps mostly call out through @nestjs/axios's HttpService — and in Node, axios uses the http adapter, not fetch, so those calls are invisible to the fetch patch. Pass an axios source to capture them too, via axios's public interceptor API (no monkey-patching). Both paths share the same content shape, tags, and family-hash, so axios and fetch entries are indistinguishable in the dashboard.
@nestjs/axios — resolve HttpService lazily. HttpService isn't constructed until the container is up, so use the custom-source instrument hook to grab axiosRef at register time from ctx.moduleRef:
import { TelescopeModule, HttpClientWatcher } from '@dudousxd/nestjs-telescope';
import { HttpService } from '@nestjs/axios';
TelescopeModule.forRoot({
watchers: [
new HttpClientWatcher({
slowMs: 1000,
axios: {
instrument(attach, ctx) {
const http = ctx.moduleRef.get(HttpService, { strict: false });
attach(http.axiosRef); // wire interceptors onto the real instance
},
},
}),
],
});Plain axios instance — hand it over directly:
import axios from 'axios';
import { TelescopeModule, HttpClientWatcher } from '@dudousxd/nestjs-telescope';
const client = axios.create({ baseURL: 'https://api.example.com' });
TelescopeModule.forRoot({ watchers: [new HttpClientWatcher({ axios: client })] });On a non-2xx response the entry records error.response.status; on a transport failure (no response) it records statusCode: null (tagged failed). Either way the rejection is re-thrown, so your error handling is untouched. Instrumentation is idempotent per axios instance, so sharing one instance across watchers never double-records.
Double-capture edge case: if you explicitly configure axios with the fetch adapter (Node's default is the http adapter), a single call could record on both the axios and fetch paths. Keep the http adapter, or pass only one source.
Headless API
| Method & path | Returns |
|---|---|
GET /telescope/api/entries | A page of entries (filter by type, tag, batch, family, time). |
GET /telescope/api/entries/:id | One entry plus its correlated batch. |
GET /telescope/api/pulse?window=1h | Per-type counts, slowest entries, top exceptions, N+1 occurrences. |
GET /telescope/api/queues?window=1h | Per-queue throughput, runtime/wait-time percentiles, failure rate. |
GET /telescope/api/timeseries?window=1h&buckets=60 | Bucketed throughput — the sparkline data. |
GET /telescope/api/health | Telescope's own self-overhead (capture cost, buffer, flush, drops). |
Key options
| Option | Description |
|---|---|
enabled | Master switch. Compute inline (e.g. NODE_ENV !== 'production'). |
storage | StorageProvider (or factory). Defaults to the embedded SQLite store. |
watchers | Built-in + custom watchers; each individually configurable. |
authorizer | Gates the API. Denies in production by default until you supply one. |
authorizeAction | Separate, default-deny gate for queue mutations. |
dashboardAuth | Signed-cookie login for the dashboard — see Dashboard auth. |
redact | Deep-redact configured headers / query / body paths (default sensitive keys always run). |
prune | Retention window driving the scheduled pruner (e.g. { after: '24h' }). Supports perType for per-type cutoffs (e.g. { after: '5m', perType: { exception: '7d' } }). |
archive | Export a type's entries to a sink before the pruner deletes them — see Archiving before prune. |
pulse | Health-snapshot tuning. slowRouteMs (default 1000) is the p99 a route must reach to count as a slow-request hotspot — matches the slow tag threshold, so a quiet host shows no false hotspots. |
traceContext / traceLink | OpenTelemetry trace stamping + deep-link template — see -otel. |
queueManagers | Live queue managers — see queue managers. |
mcp | Optional MCP server for coding agents — true (dev-only) or { token }. Disabled by default. See MCP server. |
overloadProtection | Pause capture under event-loop pressure. true (default, 200ms p99), false, or { maxEventLoopLagMs }. See Performance. |
See Capture & correlation for the recorder pipeline and the Entry / Watcher / StorageProvider SPIs.
Packages
The full suite — core, the dashboard, watchers, storage adapters, queue managers, the OpenTelemetry bridge, the AI exception diagnoser, and test utilities. Install only what your stack needs.
@dudousxd/nestjs-telescope-ui (dashboard)
The bundled dashboard SPA served by a NestJS module — plus the composable React components, hooks, and typed client to build your own admin.