NestJS Profiler
Packages

nest-profiler-routes

Inspect the application routing table in Symfony-Routing-style Discover views, one per transport.

@eleven-labs/nest-profiler-routes

@eleven-labs/nest-profiler-routes adds the Discover views to the profiler home page — a Symfony-Routing-style view of the application's routing table, with one sidebar view per transport (Discover / HTTP, Discover / GraphQL, Discover / Commands, Discover / RabbitMQ) so each transport's table is a subject of its own. Every registered route is listed with its HTTP method, full path and controller/handler; a lock marks routes protected by a guard. Expanding a route reveals its description, guards, path params, query params, request headers, and the body DTO (class name, decorated properties, TypeScript types and, when class-validator is installed, the validation rules). Non-HTTP sources describe their inputs with their own labels — CLI commands list Arguments and Options with each option's description, GraphQL fields list Arguments.

A transport that discovered nothing gets no view at all, so the sidebar only ever lists what the application actually registered.

Discover / HTTP view — the application routing table with per-route inputs, body DTOs and a lock on guarded routes

Installation

pnpm add @eleven-labs/nest-profiler-routes

Peer dependencies: @eleven-labs/nest-profiler. class-validator is an optional peer — install it to surface DTO properties and validation rules; without it, a body DTO shows only its class name.

Setup

app.module.ts
import { ConditionalModule } from '@nestjs/config';
import { RoutesCollectorModule } from '@eleven-labs/nest-profiler-routes';

const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';

@Module({
  imports: [ConditionalModule.registerWhen(RoutesCollectorModule.forRoot(), isProfilerEnabled)],
})
export class AppModule {}

Enabling / disabling — gate the collector with ConditionalModule.registerWhen(..., isProfilerEnabled) so it loads only when the profiler is on, or pass RoutesCollectorModule.forRoot({ enabled: false }). Wire the core ProfilerModule once at the root (add its ProfilerNoopModule fallback only if you inject TracerService directly) — see the example app.

What it collects

At application startup, every registered route is discovered and grouped by transport, and each group becomes its own Discover view — keyed discover-<transport> in the ?view= parameter, so it can never collide with the same-protocol profile list (?view=graphql stays the GraphQL list, ?view=discover-graphql its routing table). The core ships a built-in HTTP source; other transport packages contribute their own — GraphQL resolvers (@eleven-labs/nest-profiler-graphql), RabbitMQ subscribers (@eleven-labs/nest-profiler-rabbitmq) and CLI commands (@eleven-labs/nest-profiler-commander) — by registering a ProfilerDiscoverSource with the core, so they appear automatically when installed.

Each REST route is introspected from its decorator metadata:

  • Guards — the guard classes from @UseGuards() on the controller and/or handler (e.g. an authentication guard); guarded routes show a lock. Only route-level guards are visible — a global APP_GUARD is not attached per handler.
  • Path params — from the route path (/users/:idid).
  • Query params — from @Query('name') and whole-object @Query() DTOs.
  • Headers — from @Headers('name').
  • Body DTO — from @Body(): the DTO class name, its top-level decorated properties with their TypeScript types, and (with class-validator) the validation rules per property.

Introspection is top-level only: a property that is itself a DTO surfaces as its class name rather than being expanded.

Sources whose inputs are not HTTP-shaped use DiscoverInputs.groups instead — a list of { label, items } sections, each item being a { name, description?, required?, defaultValue? }. That is how the CLI source lists Arguments and Options, and the GraphQL source its field Arguments, without borrowing an HTTP label. A route may also carry a description, rendered above its inputs.

Contributing a custom route source

Any package can add its own Discover view by registering a ProfilerDiscoverSource with the core (mirroring how entrypoint types are registered). Each DiscoverGroup it returns becomes one view; set itemLabel when its entries are not routes, so the count reads 3 commands rather than 3 routes:

import { ModuleRef } from '@nestjs/core';
import { ProfilerCoreService } from '@eleven-labs/nest-profiler';
import type { ProfilerDiscoverSource } from '@eleven-labs/nest-profiler';

// in a module lifecycle hook:
const core = this.moduleRef.get(ProfilerCoreService, { strict: false });
core.registerDiscoverSource(mySource satisfies ProfilerDiscoverSource);

License

MIT © Eleven Labs

Powered & maintained by

On this page