NestJS Packages

Getting started

Install and configure @eleven-labs/nest-profiler in a NestJS application.

Requirements

  • Node.js 22 or newer
  • A NestJS 11 application
  • Any package manager (the examples below use pnpm; npm and yarn work too)

Installation

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

The core package declares @nestjs/common, @nestjs/core, nestjs-cls, reflect-metadata, and rxjs as peer dependencies — a NestJS application already provides all of them except nestjs-cls, which is why it is installed explicitly above.

Configure the core module

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

@Module({
  imports: [
    ProfilerModule.forRoot({
      isGlobal: true,
      enabled: process.env.NODE_ENV !== 'production',
    }),
  ],
})
export class AppModule {}

Async configuration

Use forRootAsync when your options depend on injected providers such as ConfigService. One field is different: enabled stays at the top level, outside useFactory. It decides whether the profiler's providers are registered at all, so it must be known synchronously at bootstrap — it cannot be resolved from the async factory.

app.module.ts
ProfilerModule.forRootAsync({
  // Static bootstrap flag — evaluated before the factory runs.
  enabled: process.env.NODE_ENV !== 'production',
  useFactory: (config: ConfigService) => ({
    storageType: config.get('PROFILER_STORAGE_TYPE', 'memory'),
  }),
  inject: [ConfigService],
});

Enable log capture

Wrap the NestJS logger in main.ts so that every log entry is captured in the active request profile:

main.ts
import { ConsoleLogger } from '@nestjs/common';
import { ProfilerService } from '@eleven-labs/nest-profiler';

const app = await NestFactory.create(AppModule, { bufferLogs: true });
const profilerService = app.get(ProfilerService);
app.useLogger(profilerService.createLogger(new ConsoleLogger('MyApp')));
await app.listen(3000);

Optional collector packages

Each collector is a separate package installed alongside the core. Import each one in the feature module it instruments — not in the root module — so the dependency is co-located with the code it observes.

PackagePanelPrerequisite
@eleven-labs/nest-profiler-typeormDatabaseTypeOrmModule.forRoot()
@eleven-labs/nest-profiler-mikro-ormDatabaseMikroOrmModule.forRoot()
@eleven-labs/nest-profiler-mongooseMongoDBMongooseModule.forRoot() connection
@eleven-labs/nest-profiler-axiosHTTP ClientHttpModule in the same module
@eleven-labs/nest-profiler-cacheCacheCacheModule.register({ isGlobal: true })
@eleven-labs/nest-profiler-authSecurityGuard or middleware that sets request.user
@eleven-labs/nest-profiler-configConfigConfigModule.forRoot({ load: [...] }) with registerAs factories
@eleven-labs/nest-profiler-validatorValidatorA validator: class-validator (default) or another (e.g. nestjs-zod)
@eleven-labs/nest-profiler-graphqlGraphQL@nestjs/graphql + a driver + context factory configured
@eleven-labs/nest-profiler-commanderCommandnest-commander + a CLI bootstrap (CommandFactory)

Module-per-collector pattern

Each optional collector is registered in the feature module it instruments:

products/products.module.ts — with TypeORM
import { TypeOrmCollectorModule } from '@eleven-labs/nest-profiler-typeorm';

@Module({
  imports: [
    TypeOrmModule.forFeature([Product]),
    TypeOrmCollectorModule.forRoot({ slowQueryThreshold: 50 }),
  ],
})
export class ProductsModule {}
products/products.module.ts — with MikroORM
import { MikroOrmCollectorModule } from '@eleven-labs/nest-profiler-mikro-orm';

@Module({
  imports: [
    MikroOrmModule.forFeature([Product]),
    MikroOrmCollectorModule.forRoot({ slowQueryThreshold: 50 }),
  ],
})
export class ProductsModule {}
posts/posts.module.ts
import { HttpModule } from '@nestjs/axios';
import { AxiosCollectorModule } from '@eleven-labs/nest-profiler-axios';
import { CacheCollectorModule } from '@eleven-labs/nest-profiler-cache';

@Module({
  imports: [
    HttpModule, // prerequisite for AxiosCollectorModule
    AxiosCollectorModule.forRoot(),
    CacheCollectorModule.forRoot(),
  ],
})
export class PostsModule {}
auth/auth.module.ts
import { AuthCollectorModule } from '@eleven-labs/nest-profiler-auth';

@Module({
  imports: [AuthCollectorModule.forRoot({ maskUserFields: ['password'] })],
})
export class AuthModule {}

Collectors that install a global APP_PIPE or read global providers should remain in the root module:

app.module.ts
import { ConfigCollectorModule } from '@eleven-labs/nest-profiler-config';
import { ValidatorCollectorModule } from '@eleven-labs/nest-profiler-validator';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true, load: [appConfig, dbConfig] }),
    CacheModule.register({ isGlobal: true }),
    ProfilerModule.forRoot({ isGlobal: true, enabled: process.env.NODE_ENV !== 'production' }),
    ConfigCollectorModule.forRoot({
      enabled: process.env.NODE_ENV !== 'production',
      maskKeys: ['database.password'],
    }),
    ValidatorCollectorModule.forRoot({
      enabled: process.env.NODE_ENV !== 'production',
      validationPipeOptions: { whitelist: true, transform: true },
    }),
    // feature modules:
    ProductsModule,
    PostsModule,
    AuthModule,
  ],
})
export class AppModule {}

GraphQL support

GraphQL profiling is opt-in via the dedicated @eleven-labs/nest-profiler-graphql package. The core ProfilerModule is HTTP-only — without this package, GraphQL requests pass through without profiling and without errors.

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

Import ProfilerGraphQLModule alongside ProfilerModule, then configure the context factory for your driver so the profiler can access the underlying HTTP request:

app.module.ts — Apollo (Express / Fastify) or graphql-yoga
import { ProfilerGraphQLModule } from '@eleven-labs/nest-profiler-graphql';

@Module({
  imports: [
    ProfilerModule.forRoot({ isGlobal: true }),
    ProfilerGraphQLModule.forRoot(),
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: true,
      context: ({ req }) => ({ req }), // required — exposes the request to the profiler
    }),
  ],
})
export class AppModule {}
app.module.ts — Mercurius (Fastify)
GraphQLModule.forRoot<MercuriusDriverConfig>({
  driver: MercuriusDriver,
  autoSchemaFile: true,
  context: ({ request }) => ({ request }), // Mercurius uses `request` instead of `req`
});

Without the context factory, the profiler silently falls back to passthrough for GraphQL resolvers. Subscriptions (WebSocket transport) are not profiled at this time.

Each captured GraphQL request appears in /_profiler with a GQL badge and the operation type. The Request tab shows the operation type, operation name, field name, syntax-highlighted query, and variables.

Custom protocol adapters

ProfilerModule exports an IContextAdapter interface and a PROFILER_CONTEXT_ADAPTERS multi-token that let you profile any NestJS execution context beyond HTTP — gRPC, Kafka, WebSockets, and more. @eleven-labs/nest-profiler-graphql is the reference implementation. See the nest-profiler package guide for a full adapter example.

Storage backends

// Default — in-memory LRU, cleared on restart
ProfilerModule.forRoot({ storageType: 'memory', maxProfiles: 100 });

// File — persists to .profiler/ as JSON files, survives restarts
ProfilerModule.forRoot({ storageType: 'file', storagePath: '.profiler' });

Add .profiler/ to .gitignore when using file storage.

Test with the example application

pnpm example:dev

Make requests to exercise each collector:

curl http://localhost:3000/products       # TypeORM SELECT (auto-seeded at startup)
curl http://localhost:3000/posts          # Axios GET + cache SET
curl http://localhost:3000/posts          # cache HIT
curl http://localhost:3000/auth/token     # get demo JWT
curl -H "Authorization: Bearer <token>" http://localhost:3000/auth/me  # Auth
curl -X POST http://localhost:3000/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"Hi","body":"too short"}'  # Validator violations
curl http://localhost:3000/slow            # Timeline spans

# GraphQL — requires FEATURE_GRAPHQL=true (default)
# Query with operation name
curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "operationName": "GetBooks",
    "query": "query GetBooks { books { id title author publishedYear } }"
  }'

# Query with variable
curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "operationName": "GetBook",
    "query": "query GetBook($id: ID!) { book(id: $id) { id title author } }",
    "variables": { "id": "1" }
  }'

# Mutation with operation name and variables
curl -X POST http://localhost:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "operationName": "CreateBook",
    "query": "mutation CreateBook($title: String!, $author: String!, $publishedYear: Int) { createBook(input: { title: $title, author: $author, publishedYear: $publishedYear }) { id title author } }",
    "variables": { "title": "NestJS in Action", "author": "John Doe", "publishedYear": 2024 }
  }'

Open http://localhost:3000/_profiler to browse all collected profiles.

On this page