NestJS Profiler
Packages

nest-profiler-graphql

Profile GraphQL queries and mutations across Apollo, Mercurius and yoga.

@eleven-labs/nest-profiler-graphql

@eleven-labs/nest-profiler-graphql captures GraphQL queries and mutations and displays them in their own GraphQL sidebar view, each with a dedicated GraphQL detail tab (operation, query, variables and response).

GraphQL view showing GQL MUTATION and GQL QUERY badges alongside the operation name and status

GraphQL detail tab showing operation type, operation name, syntax-highlighted query and variables

Installation

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

Setup

Register GraphQLCollectorModule next to ProfilerModule in your dev-only profiling bundle:

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

@Module({
  imports: [ProfilerModule.forRoot({ isGlobal: true }), GraphQLCollectorModule.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).

Your GraphQLModule stays in your own application module. The only requirement is that its context exposes the HTTP request — plain application config, so it ships in production unchanged.

Apollo Server (Express or Fastify)

app.module.ts
@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: true,
      // Required — exposes the Express/Fastify request so the profiler can
      // store and recover the profile across the async context boundary.
      context: ({ req }) => ({ req }),
    }),
  ],
})
export class AppModule {}

Mercurius (Fastify)

GraphQLModule.forRoot<MercuriusDriverConfig>({
  driver: MercuriusDriver,
  autoSchemaFile: true,
  // Mercurius uses `request` instead of `req`
  context: ({ request }) => ({ request }),
});

graphql-yoga (Express or Fastify)

GraphQLModule.forRoot<YogaDriverConfig>({
  driver: YogaDriver,
  autoSchemaFile: true,
  context: ({ req }) => ({ req }),
});

Ignoring playground and introspection requests

The playground and introspection requests are profiled by default. Use the ignoreRequest option of ProfilerModule together with the pre-built filters from this package to exclude them (both calls live in the profiling bundle):

import { ProfilerModule, combineFilters } from '@eleven-labs/nest-profiler';
import {
  GraphQLCollectorModule,
  ignoreGraphQLPlayground,
  ignoreGraphQLIntrospection,
} from '@eleven-labs/nest-profiler-graphql';

ProfilerModule.forRoot({
  isGlobal: true,
  ignoreRequest: combineFilters(ignoreGraphQLPlayground, ignoreGraphQLIntrospection),
}),
GraphQLCollectorModule.forRoot(),
FilterSkips
ignoreGraphQLPlaygroundGET /graphql with Accept: text/html — the Sandbox UI page load
ignoreGraphQLIntrospectionAny POST with operationName: IntrospectionQuery or a query referencing __schema / __type

What is captured

Each profiled GraphQL request shows a GQL badge in /_profiler and records:

FieldDescription
operationTypequery, mutation, or subscription
operationNameNamed operation (e.g. GetBooks), if provided
fieldNameEntry-point resolver field
queryThe full GraphQL document (formatted)
variablesVariables object

Registering this module installs the graphql entrypoint type: GraphQL operations get their own GraphQL view on /_profiler, with a filter bar including an Operation filter (query / mutation / subscription).

GraphQL-level errors (schema validation failures, resolver errors) appear in the Exceptions tab with an amber GraphQLError badge, distinct from NestJS runtime exceptions. Since GraphQL names every error GraphQLError, its extensions.code is shown alongside — that code is what the Exception filter lists and what decides whether the operation counts as an error.

Exceptions tab showing an amber GraphQLError badge with its BAD_REQUEST code, validation message and location

Options

GraphQLCollectorModule.forRoot(options) accepts:

OptionDefaultDescription
enabledtrueEnable GraphQL profiling.
errorINTERNAL_SERVER_ERRORWhat counts as a failed operation. A GraphQL response is 200 even when it failed, so statuses say nothing and extensions.code takes their role.

A BAD_REQUEST (what the Nest Apollo driver emits for a rejected mutation), BAD_USER_INPUT, UNAUTHENTICATED or NOT_FOUND is the schema answering correctly — GraphQL's equivalent of a 4xx — so none of them is an error by default. An error carrying no code counts, as an unmapped throw is a genuine failure.

// Here, a failed login is an incident worth surfacing.
GraphQLCollectorModule.forRoot({
  error: { codes: ['INTERNAL_SERVER_ERROR', 'UNAUTHENTICATED'] },
});

Use forRootAsync to resolve the options from ConfigService. See What counts as an error.

How it works

GraphQLCollectorModule registers GraphQLContextAdapter with ProfilerCoreService on module init. The adapter supports all NestJS GraphQL drivers that expose the HTTP request in the execution context:

  • Apollo (Express / Fastify): looks for gqlCtx.req
  • Mercurius (Fastify): looks for gqlCtx.request

A middleware finish hook also captures GraphQL errors for requests that Apollo handles without calling any resolver (e.g. schema validation failures), ensuring those profiles still appear in /_profiler.

Custom protocol adapters

This package is the reference implementation of the IContextAdapter pattern from @eleven-labs/nest-profiler. You can use the same pattern to profile gRPC, Kafka, WebSockets, or any other NestJS execution context — see the @eleven-labs/nest-profiler documentation for a full example.

Powered & maintained by

On this page