DEV Community

Cover image for How to setup Prometheus Metrics for NestJS GraphQL
Shinigami
Shinigami

Posted on

How to setup Prometheus Metrics for NestJS GraphQL

TLDR: https://github.com/Shinigami92/nestjs-prometheus-graphql


Start of this week (14. Nov 2022) I needed to setup Prometheus + Grafana for an API-Gateway in my company.

I was afraid that nearly nothing already exists out there šŸ™€ and I only had this (spoiler: broken) guide https://hodovi.cc/blog/nestjs-apollo-graphql-prometheus-metrics-and-grafana-dashboards

The whole code examples on that blog article are not just copy&paste-able but also stale/broken.

So I needed to put some hard work into how to wire everything up and to safe hopefully some work for other devs like me, I created now a sample minimal reproducible repository and this dev.to article.

If this helped you at any time, feel free to sponsor me a cup of coffee ā˜•ļø PayPal or via https://github.com/sponsors/Shinigami92


I assume you already have a running GraphQL NestJS app, so I will skip these steps, otherwise please follow the instructions at https://docs.nestjs.com/graphql/quick-start

First we need to install some dependencies:

npm add @willsoto/nestjs-prometheus prom-client # The general Prometheus metrics provider which will expose the endpoint /metrics
npm add apollo-metrics apollo-tracing@0.15.0 # Provides Apollo GraphQL server plugins
npm add --save-dev apollo-server-plugin-base # Contains the ApolloServerPlugin interface
Enter fullscreen mode Exit fullscreen mode

Now we will add a module file src/metrics/prom.module.ts

import { Module } from "@nestjs/common";
import { PrometheusModule } from "@willsoto/nestjs-prometheus";
import createMetricsPlugin from "apollo-metrics";
import { plugin as apolloTracingPlugin } from "apollo-tracing";
import { register } from "prom-client";

export const TRACING_PLUGIN_KEY = "TRACING_PLUGIN_KEY";
export const METRICS_PLUGIN_KEY = "METRICS_PLUGIN_KEY";

@Module({
  imports: [
    // Register the general Prometheus module
    PrometheusModule.register()
  ],
  providers: [
    {
      provide: TRACING_PLUGIN_KEY,
      // Provide the apollo tracing plugin
      // This is only needed if you also want to measure timings
      useValue: apolloTracingPlugin(),
    },
    {
      provide: METRICS_PLUGIN_KEY,
      // Provide the apollo metrics plugin
      useValue: createMetricsPlugin(register),
    },
  ],
  exports: [TRACING_PLUGIN_KEY, METRICS_PLUGIN_KEY],
})
export class PromModule {}
Enter fullscreen mode Exit fullscreen mode

The tracing plugin is only needed if you also want to measure timings of your field resolvers. At this time, there is NO Grafana Dashboard that will display this data. Maybe I will create one in the future, but for now at least just lets collect these additional data.
The tracing plugin will be used here apollo-metrics index L124

Now we need to register the PromModule in our AppModule

import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";
import { ApolloServerPlugin } from "apollo-server-plugin-base";
import { join } from "path";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { CatsModule } from "./cats/cats.module";
import {
  METRICS_PLUGIN_KEY,
  PromModule,
  TRACING_PLUGIN_KEY,
} from "./metrics/prom.module";

@Module({
  imports: [
    // Import the PromModule generally (e.g. also for other metrics like non GraphQL stuff)
    PromModule,
    CatsModule,
    GraphQLModule.forRootAsync<ApolloDriverConfig>({
      driver: ApolloDriver,
      // Import the PromModule for GraphQL
      imports: [PromModule],
      useFactory: (
        // Use provided plugins injected from below
        tracingPlugin: ApolloServerPlugin,
        metricsPlugin: ApolloServerPlugin
      ) => ({
        debug: true,
        introspection: true,
        playground: true,
        // Plugins added to apollo server
        plugins: [tracingPlugin, metricsPlugin],
        autoSchemaFile: join(process.cwd(), "src/schema.gql"),
      }),
      // We need to inject the provider keys
      inject: [TRACING_PLUGIN_KEY, METRICS_PLUGIN_KEY],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Warning: It is important to provide the plugins via that way, as otherwise if not registered to the NestJS context e.g. E2E tests will fail because the prom-client can only register a metric name once and will fail if a plugin tries to recreate a metric.


Now we are ready and just need to connect our metrics endpoint to Prometheus and then add Prometheus as Datasource for Grafana. (Please watch some other tutorials for that)

Now import the Grafana Dashboard: https://grafana.com/grafana/dashboards/16188-graphql-metrics

āš ļø But attention! āš ļø You need to reconfigure the parameter operation_name to operationName (snake_case to camelCase)

renamed operationName

The blog article https://hodovi.cc/blog/nestjs-apollo-graphql-prometheus-metrics-and-grafana-dashboards was manually using its own metric names šŸ¤·


Please let me know if this was helpful and leave some comments.


Edit

I started to edit the dashboard provided by Adin Hodovic and extended it with tracing/timing panels.

Grafana Dashboard (json export): https://github.com/Shinigami92/nestjs-prometheus-graphql/blob/main/ApolloGraphQLMetrics.grafana-dashboard.json

I tried to publish it at grafana.com dashboards, but somehow the upload does not work šŸ¤·

Top comments (2)

Collapse
 
victor_io profile image
Victor Ihuoma

Great article! Iā€™m trying to do same for a nestjs app using fastify under the hood, no graphql, how do I modify this to fit that purpose

Collapse
 
lintong profile image
Linton Galloway

Top work, this worked for me first time!