Agora

Getting Started

Install @adonis-agora/telescope, configure it into your AdonisJS app, read entries back from the headless API, then layer on persistent storage, per-technology watchers, and the dashboard.

This walks you from a bare AdonisJS app to a fully instrumented one: requests, queries, exceptions and diagnostics events captured, queryable, and visible in a dashboard.

Prerequisites

  • AdonisJS 7 (@adonisjs/core ^7.3.0).
  • Node 20.6+ (the diagnostics integration uses node:diagnostics_channel).
  • Optional but recommended: @adonis-agora/context for trace correlation and @adonis-agora/diagnostics so library events flow into Telescope.

Step 1 — Install the core

Add the package

npm i @adonis-agora/telescope
node ace configure @adonis-agora/telescope

What configure does

node ace configure @adonis-agora/telescope runs codemods that:

  1. register @adonis-agora/telescope/telescope_provider in adonisrc.ts;
  2. register TelescopeMiddleware on the server middleware stack (so it wraps the whole HTTP pipeline and observes the final response status);
  3. publish config/telescope.ts.

That is everything the core needs. With both default watchers on, every inbound request is recorded as a request entry, and every agora:<lib>:<event> diagnostics publish is recorded as a diagnostic entry.


Step 2 — Configure (optional)

The published config/telescope.ts is all-defaults — you only edit it to change behaviour:

config/telescope.ts
import { defineConfig, storage } from '@adonis-agora/telescope'

export default defineConfig({
  enabled: true,                          // master switch
  store: 'memory',                        // which driver in `stores` is active
  stores: {
    memory: storage.memory({ limit: 1000 }),   // bounded in-process ring buffer
  },
  watchers: ['request', 'diagnostics'],   // omit one to disable it
})
KeyDefaultWhat it does
enabledtrueMaster switch. false → records nothing, watchers stay dormant.
store'memory'Which named driver in stores is active (or a TelescopeStore instance).
stores{}Named drivers built with the storage factory (memory / lucid).
watchers['request', 'diagnostics']Which built-in watchers run. Omit a name to disable it.
extensions[]Extensions contributing entry types, dashboards, and data providers.

See the configuration reference for every key.


Step 3 — Hit the headless API

Telescope is headless first: the dashboard is optional, and everything it shows is served from TelescopeService. Resolve it from the container and read entries back.

app/controllers/inspector_controller.ts
import { inject } from '@adonisjs/core'
import type { HttpContext } from '@adonisjs/core/http'
import { TelescopeService } from '@adonis-agora/telescope'

@inject()
export default class InspectorController {
  constructor(private telescope: TelescopeService) {}

  async index({ response }: HttpContext) {
    return response.json(await this.telescope.list({ size: 50 }))
  }
}

@inject() on the class asks the container to construct the controller's dependencies from their constructor types, so TelescopeService arrives already resolved. A controller has no app of its own, so reaching for this.app.container.make(...) inside one will not compile.

The full surface:

MethodReturns
list(query?)Entries matching the EntryQuery, newest-first.
find(id)One Entry, or null.
byTrace(traceId)Every entry recorded under a trace, newest-first.
count()Total stored entries.
topFamilies(limit?, type?)Busiest groups by familyHash (e.g. busiest lib:event pairs).
topTags(limit?, prefix?)Most common tags, optionally by prefix.

The EntryQuery composes filters with AND: { type, tag, familyHash, traceId, before, after, search, page, size }page is 1-based and size is the page size, the same pagination pair every @adonis-agora/* library uses.

await telescope.list({ type: 'request', size: 20 })
await telescope.list({ tag: 'lib:billing', search: 'invoice' })
await telescope.byTrace('abc123')
await telescope.topFamilies(10, 'diagnostic')

Step 4 — Pick your storage

The default memory driver is a bounded ring buffer — fast, dependency-free, and lost on restart. Great for dev and tests. For a store that survives restarts and is queryable straight from your database, switch to the built-in lucid driver. It needs @adonisjs/lucid (an optional peer, imported only when selected):

npm i @adonisjs/lucid
node ace migration:run   # runs the migration configure already published

node ace configure @adonis-agora/telescope ships the create_telescope_entries_table migration alongside the config file. Then point the config at the lucid driver:

config/telescope.ts
import { defineConfig, storage } from '@adonis-agora/telescope'

export default defineConfig({
  store: 'lucid',
  stores: {
    lucid: storage.lucid(),                  // or storage.lucid({ connection: 'pg' })
  },
})

The lucid driver implements the same TelescopeStore contract and works on every Lucid dialect (sqlite / Postgres / MySQL). See Storage drivers.


Step 5 — Add per-technology watchers

The core records requests, exceptions and diagnostics. To also record every Lucid SQL query (and optionally mail / cache events), enable the watchers feature — it ships inside @adonis-agora/telescope, so re-run configure and pick Watchers:

node ace configure @adonis-agora/telescope   # pick "Watchers"

By default only the verified Lucid query watcher runs. Enable mail and cache in config/telescope_watchers.ts:

config/telescope_watchers.ts
import { defineConfig } from '@adonis-agora/telescope/watchers'

export default defineConfig({
  watchers: ['query', 'mail', 'cache'],
})

Lucid only emits db:query when the connection's debug flag is on (or a db:query listener exists at report time). Subscribing the watcher is enough to make Lucid report; setting debug: true in your DB config guarantees it.

See Watchers for each watcher's recorded shape and caveats.


Step 6 — Mount the dashboard

The core UI feature (the @adonis-agora/telescope/ui subpath) serves the JSON API and live-stream under <path>/api/* — it does not render a page. The browsable dashboard is a separate package, @adonis-agora/telescope-ui, a pre-built React SPA (no build step in your app) served under that same prefix and auth guard.

Enable both:

node ace configure @adonis-agora/telescope   # pick "UI" (JSON API + SSE)
npm i @adonis-agora/telescope-ui             # the React dashboard SPA

Then register @adonis-agora/telescope-ui/telescope_ui_dashboard_provider after the core ui_provider in adonisrc.ts. With both providers registered, the dashboard mounts at /telescope and is reachable automatically outside production. In production it is denied unless you set a credential or supply an authorize hook. See Dashboard auth.


Next steps

On this page