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 @eleven-labs/nest-profiler-graphql@alpha

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

Setup

Import GraphQLCollectorModule alongside ProfilerModule in your application module.

Apollo Server (Express or Fastify)

import { ConditionalModule } from '@nestjs/config';
import { GraphQLCollectorModule } from '@eleven-labs/nest-profiler-graphql';

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

@Module({
  imports: [
    ConditionalModule.registerWhen(GraphQLCollectorModule.forRoot(), isProfilerEnabled),
    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)

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

graphql-yoga (Express or Fastify)

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

Enabling and disabling

Enabling / disabling — gate the collector with ConditionalModule.registerWhen(..., isProfilerEnabled) as shown, so it loads only when PROFILER_ENABLED is on. Wire the core ProfilerModule once at the root — the recommended setup bundles the root-level profiler modules into a single ProfilingModule behind a ConditionalModule gate (see Enabling and disabling the profiler and the example app). A top-level enabled option is also supported as an alternative.

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:

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