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).


Installation
pnpm add -D @eleven-labs/nest-profiler-graphqlSetup
Register GraphQLCollectorModule next to ProfilerModule in your dev-only profiling bundle:
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 {}
ProfilingModuleis the dev-only bundle loaded bymain-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 inConditionalModule.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)
@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(),| Filter | Skips |
|---|---|
ignoreGraphQLPlayground | GET /graphql with Accept: text/html — the Sandbox UI page load |
ignoreGraphQLIntrospection | Any 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:
| Field | Description |
|---|---|
operationType | query, mutation, or subscription |
operationName | Named operation (e.g. GetBooks), if provided |
fieldName | Entry-point resolver field |
query | The full GraphQL document (formatted) |
variables | Variables 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.

Options
GraphQLCollectorModule.forRoot(options) accepts:
| Option | Default | Description |
|---|---|---|
enabled | true | Enable GraphQL profiling. |
error | INTERNAL_SERVER_ERROR | What 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.