NestJS Profiler
Tutorials

Set up the profiler

Install @eleven-labs/nest-profiler, enable the core module and inspect your first HTTP requests in the profiler UI.

This tutorial shows how to add @eleven-labs/nest-profiler to a NestJS application and inspect collected request data in the built-in profiler UI.

Step 1 - Install the package

pnpm add @eleven-labs/nest-profiler@alpha nestjs-cls

Alpha release

There is no stable release yet — every @eleven-labs/nest-profiler* package must be installed with the @alpha dist-tag. @latest resolves to nothing.

nestjs-cls is required for per-request context propagation.

Want the profiler in devDependencies only, with no production footprint? Install it with pnpm add -D @eleven-labs/nest-profiler@alpha nestjs-cls and follow the dev-entry split instead of the runtime gate used below.

Step 2 - Register the module

Import ProfilerModule in your root module. The recommended way is to gate it with ConditionalModule.registerWhen, so the profiler loads only when you want it:

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

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

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

If a service injects ProfilerService directly (custom startSpan, events…), also register ProfilerNoopModule.forRoot({ isGlobal: true }) gated on (env) => !isProfilerEnabled(env) so it still resolves when off. Log capture never needs it. See Enabling and disabling.

Step 3 - Enable log capture

Wrap your existing logger in main.ts so log entries appear in the profiler's Logs tab:

main.ts
import { ConsoleLogger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { createProfilerLogger } from '@eleven-labs/nest-profiler';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { bufferLogs: true });

  // createProfilerLogger is DI-free — no ProfilerService, and a pass-through when the profiler is off.
  app.useLogger(createProfilerLogger(new ConsoleLogger('MyApplication')));

  await app.listen(3000);
}

void bootstrap();

The capture is logger-agnostic and keeps structured context - message, context name and payload all land in the Logs tab whichever logger the app uses. The Log capture with context tutorial and the Log capture page cover the supported conventions in detail.

Step 4 - Make a request and inspect the profile

Start the application, then make any HTTP request:

curl -i http://localhost:3000/health

The response includes two headers:

X-Debug-Token: 550e8400-e29b-...
X-Debug-Token-Link: /_profiler/550e8400-e29b-...

Open http://localhost:3000/_profiler in your browser to see the list of recent profiles. Click a token to open the detail view with tabs:

  • Request - method, URL, headers, query params
  • Response - status code, headers
  • Performance - duration, memory delta
  • Logs - log entries captured during the request
  • Exceptions - errors thrown during the request

Step 5 - Secure the profiler (optional)

The profiler is open by default. Restrict /_profiler/* by providing your own security strategy — an authorize predicate and/or NestJS guards. A minimal bearer-token check:

app.module.ts
ProfilerModule.forRoot({
  security: {
    authorize: ({ request }) =>
      request.headers['authorization'] === `Bearer ${process.env.PROFILER_TOKEN}`,
  },
});
PROFILER_TOKEN=my-dev-secret pnpm start:dev
curl -H "Authorization: Bearer my-dev-secret" http://localhost:3000/_profiler

See Securing the UI for basic-auth, cookie/session and DI-guard recipes.

Production warning

Never enable the profiler in production. It exposes internal request data including headers, query parameters, and logs. Disable it in non-development environments.

The recommended way is to gate ProfilerModule with ConditionalModule.registerWhen, so the active module is never loaded when profiling is off:

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

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

ConditionalModule.registerWhen(
  ProfilerModule.forRootAsync({ isGlobal: true, useFactory: () => ({ maxProfiles: 100 }) }),
  isProfilerEnabled,
),

Log capture keeps working when off because createProfilerLogger is DI-free. Only if a service injects ProfilerService directly do you also register ProfilerNoopModule.forRoot({ isGlobal: true }) gated on (env) => !isProfilerEnabled(env) — it supplies a zero-dependency no-op so the injection still resolves (every method a no-op; no controller, interceptor, middleware, storage or collector).

Powered & maintained by

On this page