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.

Installation
pnpm add @eleven-labs/nest-profiler-routesPeer 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
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 passRoutesCollectorModule.forRoot({ enabled: false }). Wire the coreProfilerModuleonce at the root (add itsProfilerNoopModulefallback only if you injectTracerServicedirectly) — 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 globalAPP_GUARDis not attached per handler. - Path params — from the route path (
/users/:id→id). - 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 (withclass-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