npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pawells/nestjs-prometheus

v2.0.2

Published

NestJS Prometheus metrics module with endpoint controller

Readme

NestJS Prometheus Module

GitHub Release CI npm version Node License: MIT GitHub Sponsors

Prometheus metrics exporter for NestJS with /metrics endpoint, integrated with @pawells/nestjs-shared InstrumentationRegistry for event-based metric collection.

Installation

yarn add @pawells/nestjs-prometheus prom-client

Requirements

  • Node.js: >= 22.0.0
  • NestJS: >= 10.0.0
  • prom-client: >= 15.0.0
  • @pawells/nestjs-shared: same version

Peer Dependencies

{
  "@nestjs/common": ">=10.0.0",
  "prom-client": ">=15.0.0"
}

Quick Start

Module Setup

Import PrometheusModule in your application module:

import { Module } from '@nestjs/common';
import { PrometheusModule } from '@pawells/nestjs-prometheus';

@Module({
  imports: [PrometheusModule.ForRoot()],
})
export class AppModule {}

This automatically registers the PrometheusExporter globally and exposes metrics at GET /metrics in Prometheus text format.

How It Works

The module integrates with @pawells/nestjs-shared's InstrumentationRegistry to:

  1. Register descriptors: When a metric is registered with the InstrumentationRegistry, PrometheusExporter pre-creates the corresponding prom-client instrument (Counter, Gauge, or Histogram)
  2. Buffer values: Metric values are buffered in memory as they are recorded
  3. Flush on pull: When the /metrics endpoint is scraped, all pending values are flushed into prom-client instruments and returned in Prometheus text format
  4. Cleanup: On shutdown, the registry and internal caches are cleared

This event-based design decouples metric recording from Prometheus scraping, preventing performance overhead on high-frequency metrics.

The /metrics Endpoint

The module automatically exposes:

  • Endpoint: GET /metrics
  • Content-Type: text/plain; version=0.0.4; charset=utf-8
  • Headers: X-Robots-Tag: noindex, nofollow (prevents indexing)
  • Authentication: Protected by MetricsGuard from @pawells/nestjs-shared

Authentication

The /metrics endpoint respects the optional METRICS_API_KEY environment variable:

  • If not set: All requests are allowed
  • If set: Requires Bearer token, X-API-Key header, or ?key= query parameter
# With METRICS_API_KEY=secret123
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics
curl -H "X-API-Key: secret123" http://localhost:3000/metrics
curl "http://localhost:3000/metrics?key=secret123"

Exported Metrics

The exporter provides three types of metrics:

Node.js Default Metrics

Automatically collected via prom-client:

  • process_cpu_user_seconds_total - User CPU time
  • process_cpu_system_seconds_total - System CPU time
  • process_resident_memory_bytes - RSS memory usage
  • nodejs_eventloop_delay_seconds - Event loop delay
  • nodejs_gc_duration_seconds - Garbage collection duration
  • And many more...

Custom Metrics

Applications can register custom metrics with the InstrumentationRegistry and they will be exported automatically.

Example Metrics Output

# HELP process_cpu_user_seconds_total Total user CPU time spent in seconds.
# TYPE process_cpu_user_seconds_total counter
process_cpu_user_seconds_total 12.345

# HELP http_request_duration_seconds HTTP request duration in seconds
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.001",method="GET",route="/api/users"} 0
http_request_duration_seconds_bucket{le="0.01",method="GET",route="/api/users"} 5
http_request_duration_seconds_bucket{le="0.1",method="GET",route="/api/users"} 42

Accessing Metrics

Via Prometheus Scraper

Configure Prometheus to scrape your application:

# prometheus.yml
scrape_configs:
  - job_name: 'my-app'
    static_configs:
      - targets: ['localhost:3000']
    metrics_path: '/metrics'
    scrape_interval: 15s
    # Optional: Add auth if METRICS_API_KEY is set
    authorization:
      credentials: 'secret123'
      type: Bearer

Manual HTTP Request

curl http://localhost:3000/metrics

# With authentication
curl -H "Authorization: Bearer secret123" http://localhost:3000/metrics

Integration with @pawells/nestjs-shared HTTPMetricsInterceptor

Combine PrometheusModule with HTTPMetricsInterceptor for automatic HTTP metrics:

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { PrometheusModule } from '@pawells/nestjs-prometheus';
import { HTTPMetricsInterceptor } from '@pawells/nestjs-shared';

@Module({
  imports: [PrometheusModule.ForRoot()],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: HTTPMetricsInterceptor,
    },
  ],
})
export class AppModule {}

This automatically tracks:

  • Request count by method and route
  • Request duration (histogram with default buckets)
  • Response status codes
  • Path normalization (dynamic segments like UUIDs, MongoDB ObjectIDs, and numeric IDs are normalized to :id — this is a security measure that prevents unbounded metric cardinality, which could exhaust Prometheus memory if high-entropy path segments were used as label values)

Module API

PrometheusModule.ForRoot()

Returns a DynamicModule configured as global, enabling single import at the top level:

@Module({
  imports: [PrometheusModule.ForRoot()],
})
export class AppModule {}

Class Reference

PrometheusModule

The main NestJS module. Implements OnModuleInit and OnApplicationShutdown.

Static Methods:

  • ForRoot() - Create global module with automatic registration

Lifecycle Methods:

  • onModuleInit() - Registers the exporter with InstrumentationRegistry
  • onApplicationShutdown() - Calls Exporter.Shutdown() to clean up resources

PrometheusExporter

Implements IMetricsExporter from @pawells/nestjs-shared.

Properties:

  • SupportsEventBased - true (buffers metric values)
  • SupportsPull - true (supports pull-based retrieval)

Methods:

  • OnDescriptorRegistered(descriptor: IMetricDescriptor) - Called when a metric is registered; creates the appropriate prom-client instrument
  • OnMetricRecorded(value: IMetricValue) - Buffers a metric value to be flushed on next pull
  • GetMetrics(): Promise<string> - Flushes pending values and returns metrics in Prometheus text format
  • Shutdown(): Promise<void> - Clears registry and releases resources

Internals:

  • Maintains a prom-client Registry for instrument management
  • Buffers metric values per metric (max 1000 per metric to prevent unbounded memory growth)
  • Atomically swaps pending arrays during flush to prevent data loss on concurrent records

MetricsController

HTTP controller with single endpoint.

Methods:

  • GetMetrics(response: Response): Promise<void> - GET /metrics, protected by MetricsGuard

Advanced Patterns

Custom Metrics via InstrumentationRegistry

Register custom metrics with @pawells/nestjs-shared:

import { Injectable } from '@nestjs/common';
import { InstrumentationRegistry } from '@pawells/nestjs-shared';

@Injectable()
export class OrderService {
  constructor(private readonly registry: InstrumentationRegistry) {}

  trackOrderCreation(status: string) {
    // Register descriptor (once)
    this.registry.registerDescriptor({
      name: 'orders_created_total',
      type: 'counter',
      help: 'Total orders created',
      labelNames: ['status'],
    });

    // Record value
    this.registry.recordMetric({
      descriptor: { name: 'orders_created_total' } as MetricDescriptor,
      value: 1,
      labels: { status },
    });
  }
}

When metrics are pulled from /metrics, they are automatically exported in Prometheus format.

Metric Types

The PrometheusExporter supports these metric types (as defined in MetricDescriptor):

| Type | Mapping | Usage | |------|---------|-------| | counter | prom-client Counter | Monotonically increasing values (e.g., request count) | | gauge | prom-client Gauge | Point-in-time values (e.g., memory usage) | | updown_counter | prom-client Gauge | Values that can increase or decrease | | histogram | prom-client Histogram | Distribution of values (e.g., request latency) |

Gauge and UpDownCounter Accumulation Behavior

Gauge (gauge): Records a point-in-time snapshot value. Each recorded value overwrites the previous one for a given label set.

UpDownCounter (updown_counter): Accumulates values across scrapes. The PrometheusExporter maintains a running-total Map (gaugeValues) for each updown_counter metric, keyed by normalized label set. When a value is recorded:

  1. All values for the same label set within a single scrape interval are accumulated together
  2. The accumulated sum is then added to the persistent running total
  3. This running total persists across Prometheus scrapes (resets only on exporter shutdown)

Example: If you record +5 then +3 for the same labels in one scrape cycle, the running total increases by 8 (not just 3). On the next scrape, if you record +2, the running total increases by another 2.

Related Packages

License

MIT