NestJS Profiler
API Reference

nest-profiler-routes

Types and API of the Discover views and their discover-source extension point.

RoutesCollectorModuleOptions

Options passed to RoutesCollectorModule.forRoot().

Prop

Type

What is captured

At application startup every registered route is discovered and grouped by transport, and each group becomes its own Discover sidebar view (?view=discover-<transport>). The package ships a built-in HTTP source; other transport packages contribute their own view when installed:

  • HTTP (built-in) — every @Controller handler, with its HTTP method, full path and the inputs read from @Param, @Query, @Headers and @Body.
  • GraphQL — every query, mutation and subscription field with its argument names (@eleven-labs/nest-profiler-graphql).
  • RabbitMQ — the declared topology (connections, exchanges, queues, exchange bindings) followed by every @RabbitSubscribe / @RabbitRPC consumer with its full subscription: queue, exchange, routing keys, connection, queue options and error behaviour (@eleven-labs/nest-profiler-rabbitmq).
  • Commands — every nest-commander @Command() with its name and --option flags (@eleven-labs/nest-profiler-commander).

A REST route also shows the guards protecting it — the classes from @UseGuards() on the controller and/or handler (e.g. an authentication guard) — with a lock marking guarded routes. Only route-level guards are reflected; a global APP_GUARD is not attached per handler.

For a REST route, each @Body() DTO is introspected to its top-level decorated properties: the property name, its TypeScript type and, when class-validator is installed, the validation rules. Introspection is top-level only — a property that is itself a DTO surfaces as its class name rather than being expanded.

Public exports

import {
  RoutesCollectorModule,
  RoutesCollector,
  HttpDiscoverSource,
  describeHandlerParams,
  discoverViewKey,
  handlerHasRouteArgs,
  readRouteGuards,
  DISCOVER_GROUP,
  DISCOVER_GROUP_LABEL,
  ROUTES_COLLECTOR_OPTIONS,
} from '@eleven-labs/nest-profiler-routes';

import type {
  RoutesCollectorModuleOptions,
  RoutesCollectorModuleAsyncOptions,
  RoutesCollectorData,
} from '@eleven-labs/nest-profiler-routes';

The ProfilerDiscoverSource, DiscoverGroup, DiscoverEntry and DiscoverInputs types — the extension point every source implements — are exported from the core package, together with DiscoverInputGroup / DiscoverInputItem, which describe inputs that are not HTTP-shaped (CLI arguments and options, GraphQL field arguments), and DiscoverSection / DiscoverSectionItem, which describe what a transport declared beyond its entries (the RabbitMQ topology, for instance):

import type {
  ProfilerDiscoverSource,
  DiscoverGroup,
  DiscoverEntry,
  DiscoverInputs,
  DiscoverInputGroup,
  DiscoverInputItem,
  DiscoverSection,
  DiscoverSectionItem,
} from '@eleven-labs/nest-profiler';

Setup

profiling/profiling.module.ts
import { Module } from '@nestjs/common';
import { ProfilerModule } from '@eleven-labs/nest-profiler';
import { RoutesCollectorModule } from '@eleven-labs/nest-profiler-routes';

@Module({
  imports: [ProfilerModule.forRoot({ isGlobal: true }), RoutesCollectorModule.forRoot()],
})
export class ProfilingModule {}

ProfilingModule is the dev-only bundle loaded by main-dev.ts — see Enabling and disabling the profiler. If the profiler is installed as a production dependency behind the runtime gate, wrap the same call in ConditionalModule.registerWhen(..., isProfilerEnabled).

Contributing a custom discover source

Register a ProfilerDiscoverSource with the core to add your own Discover view — the same mechanism the GraphQL, RabbitMQ and CLI packages use. Each DiscoverGroup returned becomes one view; set itemLabel when its entries are not routes, so the count reads 3 commands rather than 3 routes. Resolve the core via ModuleRef (a dynamic module cannot reliably inject a provider exported by another dynamic module):

import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { ProfilerCoreService } from '@eleven-labs/nest-profiler';
import type { ProfilerDiscoverSource, DiscoverGroup } from '@eleven-labs/nest-profiler';

@Injectable()
export class MyDiscoverSource implements ProfilerDiscoverSource, OnApplicationBootstrap {
  readonly type = 'my-transport';

  constructor(private readonly moduleRef: ModuleRef) {}

  onApplicationBootstrap(): void {
    try {
      this.moduleRef.get(ProfilerCoreService, { strict: false }).registerDiscoverSource(this);
    } catch {
      // profiler not configured — nothing to register with
    }
  }

  collect(): DiscoverGroup {
    return { source: 'my-transport', label: 'My transport', itemLabel: 'endpoint', entries: [] };
  }
}

Declaring more than entries

A transport's static surface is not always a list of entries. Add sections to the group and each becomes a titled block rendered above the entry list — an item carries a name, an optional kind badge, a one-line detail, boolean flags and free-form attributes. This is how the RabbitMQ source shows the exchanges and queues the application declared, including the ones no handler consumes:

collect(): DiscoverGroup {
  return {
    source: 'my-transport',
    label: 'My transport',
    itemLabel: 'endpoint',
    entries: [],
    sections: [
      {
        label: 'Queues',
        itemLabel: 'queue',
        items: [
          {
            name: 'api-tts.narration',
            detail: '← articles.events (published.*)',
            flags: ['durable'],
            attributes: { 'x-dead-letter-exchange': 'tts.dead-letter' },
          },
        ],
      },
    ],
  };
}

A group whose entries are empty but whose sections hold something still gets its view — a broker an application only publishes to is worth showing.

Powered & maintained by

On this page