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

@fluojs/metrics

v3.0.3

Published

Prometheus metrics exposure with isolated registries and low-cardinality HTTP metric middleware for Fluo.

Readme

@fluojs/metrics

Prometheus metrics exposure for fluo applications, including framework-aware HTTP metrics and platform telemetry.

Table of Contents

Installation

pnpm add @fluojs/metrics

Requirements

@fluojs/metrics requires Node.js >=24.0.0 <27 as its package-owned support contract.

When to Use

  • when your app should expose a /metrics endpoint for Prometheus-compatible scraping
  • when HTTP latency and request counts should be instrumented without hand-written middleware
  • when application telemetry should stay aligned with fluo readiness and health state

Quick Start

This module registration fragment belongs in an existing HTTP app. Follow Application Bootstrap Protocol for the server entrypoint and decorator configuration. Protect /metrics separately at the deployment boundary as described below.

import { MetricsModule } from '@fluojs/metrics';
import { Module } from '@fluojs/core';

@Module({
  imports: [MetricsModule.forRoot({ http: true })],
})
class AppModule {}

MetricsModule.forRoot() exposes GET /metrics by default. Pass http: true (or an http options object) when you want the module to install HTTP request instrumentation middleware. When HTTP instrumentation is enabled, the module records request totals, error counts, and request duration. For production deployments, make the scrape endpoint boundary explicit: either disable it with path: false until a platform-level proxy is in place, or attach dedicated endpoint middleware.

The scrape endpoint returns the active prom-client registry output with that registry's Prometheus content type. MetricsModule.forRoot() creates an isolated registry for each application bootstrap; reusing the same dynamic module class for another bootstrap receives fresh isolated metrics state. Configure METRICS_REGISTRY at bootstrap only when framework metrics and application-defined metrics intentionally share one scrape surface. The former registry module option is removed: migrate each independent application by supplying its own bootstrap provider.

Public Responsibilities

| Surface | Responsibility | Boundary | | --- | --- | --- | | MetricsModule.forRoot(...) | Wires the Prometheus scrape endpoint, default metrics, optional HTTP instrumentation, platform telemetry, and registry ownership. | provider currently accepts only 'prometheus'; path: false disables the scrape route and route-scoped endpoint middleware. | | MetricsService | Application-facing facade for custom Counter, Gauge, and Histogram metrics on the active registry, plus getRegistry() for deliberate advanced registry sharing. | MetricsService is non-global: inject it from a module that directly imports a MetricsModule.forRoot(...) registration or imports a module that re-exports MetricsService; unrelated sibling modules do not receive it automatically. Use collector helpers for business/application metrics. Use getRegistry() only when an integration must hand the active prom-client Registry to code that cannot receive METRICS_REGISTRY at bootstrap. | | METRICS_REGISTRY | Bootstrap provider token for a shared prom-client Registry. | Supply it only through FluoFactory.create(..., { providers }); it is the sole shared-registry input. | | Registry | prom-client's Registry constructor re-exported by @fluojs/metrics/integration for shared-registry setups. | It is the same Prometheus registry implementation; duplicate metric names still fail according to Prometheus semantics. | | METER_PROVIDER / PrometheusMeterProvider / meter types | Low-level meter bridge for first-party package integrations that need a provider token or backend-neutral counter/gauge/histogram facade. | Application code usually does not need this token unless it is composing package-level integrations; the only bundled provider backend today is Prometheus. | | middleware | Module-level middleware that participates in the module middleware chain after framework HTTP metrics and endpoint-scoped middleware. | It is not route-scoped; use endpointMiddleware when only the scrape route should be protected. | | endpointMiddleware | Class-based @fluojs/http middleware constructors bound only to the configured scrape endpoint. | Ignored only when path: false; any string path, including '', remains an active endpoint path. Functions or global middleware declarations are outside this option's contract. |

Common Patterns

Normalize HTTP path labels

MetricsModule.forRoot({
  http: {
    pathLabelMode: 'template',
    unknownPathLabel: 'UNKNOWN',
  },
});

pathLabelMode: 'raw' is an unsafe opt-in. You must pass allowUnsafeRawPathLabelMode: true only when you can prove the path space is bounded.

Custom path label normalization

MetricsModule.forRoot({
  http: {
    pathLabelNormalizer: ({ path }) => (path.startsWith('/api/v1') ? '/api/v1/:resource' : path),
  },
});

Configure HTTP duration histogram buckets

MetricsModule.forRoot({
  http: {
    durationHistogramBuckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
  },
});

durationHistogramBuckets replaces the built-in HTTP request duration histogram's prom-client defaults. Values are measured in seconds and must fit the latency range you intend to alert on. Each boundary must be finite and strictly increasing; invalid configuration is rejected during setup.

Protect or disable the metrics endpoint

import { ForbiddenException, type MiddlewareContext, type Next } from '@fluojs/http';

class MetricsTokenMiddleware {
  async handle(context: MiddlewareContext, next: Next): Promise<void> {
    if (context.request.headers['x-metrics-token'] !== 'secret-token') {
      throw new ForbiddenException('Metrics endpoint requires x-metrics-token.');
    }

    await next();
  }
}

MetricsModule.forRoot({
  endpointMiddleware: [MetricsTokenMiddleware],
});

MetricsModule.forRoot({
  path: false,
});

endpointMiddleware accepts class-based @fluojs/http middleware constructors and binds them only to the metrics scrape endpoint. Middleware functions or global middleware declarations are not the package contract for this option. middleware remains module-level middleware and runs as part of the module chain after endpoint-scoped middleware, while endpointMiddleware is skipped entirely when path: false disables the scrape route. When HTTP instrumentation is enabled, failures thrown by endpoint middleware are recorded in the built-in HTTP request and error collectors.

Create custom metrics once and reuse them

MetricsService.counter(...), gauge(...), and histogram(...) create Prometheus collectors on the active registry. MetricsService is non-global: the provider or controller that injects it must belong to a module that directly imports a MetricsModule.forRoot(...) registration or imports a module that re-exports MetricsService; unrelated sibling modules do not receive it automatically. Create each custom metric once during provider construction or application startup, then reuse the returned collector when business actions occur.

import { Inject } from '@fluojs/core';
import { MetricsService } from '@fluojs/metrics';

@Inject(MetricsService)
class OrdersService {
  private readonly ordersCreated: ReturnType<MetricsService['counter']>;

  constructor(metrics: MetricsService) {
    this.ordersCreated = metrics.counter({
      name: 'orders_created_total',
      help: 'Total orders created',
    });
  }

  recordOrderCreated(): void {
    this.ordersCreated.inc();
  }
}

Calling MetricsService.counter(...) again with the same name recreates the collector and follows Prometheus' duplicate-name failure behavior. Store and reuse the collector instead of creating it inside each request or command handler.

MetricsService.getRegistry() returns the same active prom-client Registry used by the module scrape endpoint, built-in HTTP collectors, platform telemetry, and custom collectors created through the service. When you own the bootstrap, configure an explicit shared registry with the public METRICS_REGISTRY token. Use getRegistry() for advanced integrations that receive MetricsService through DI and need to register a third-party Prometheus collector on the already active registry.

Share one registry for framework and app metrics

If your application chooses to import raw collectors such as Counter or Registry directly from prom-client, add it to your application's dependencies:

pnpm add prom-client

@fluojs/metrics uses prom-client internally, but its dependency does not make prom-client a supported transitive import for your application. The setup below uses the Registry re-export from @fluojs/metrics/integration, so it does not require that direct dependency.

import { Module } from '@fluojs/core';
import { METRICS_REGISTRY, MetricsModule } from '@fluojs/metrics';
import { Registry } from '@fluojs/metrics/integration';
import { FluoFactory } from '@fluojs/runtime';

const registry = new Registry();

@Module({
  imports: [MetricsModule.forRoot({ http: true })],
})
class AppModule { }

const app = await FluoFactory.create(AppModule, {
  providers: [{ provide: METRICS_REGISTRY, useValue: registry }],
});

Registry is re-exported by @fluojs/metrics/integration, so this setup needs no direct prom-client dependency. Create application collectors through the MetricsService pattern above.

When multiple metrics module instances intentionally share the same registry, built-in HTTP metrics reuse the existing http_requests_total, http_errors_total, and http_request_duration_seconds collectors instead of registering duplicate framework metrics only when their framework ownership, label schema, and effective HTTP instrumentation configuration match. Compatibility includes pathLabelMode, the exact pathLabelNormalizer function reference, unknownPathLabel fallback semantics, and ordered durationHistogramBuckets values, so incompatible module instances fail fast instead of mixing different HTTP series policies into one collector set. Built-in platform telemetry gauges follow the same ownership rule: module-created fluo_component_ready, fluo_component_health, and fluo_metrics_registry_mode gauges are reused only when their framework ownership and label schema match. Platform telemetry state is tracked per reused registry, so a later scrape replaces stale module-owned component readiness and health series from an earlier module instance before metrics are returned. The registry scrape wrapper keeps using the latest active module registration and restores the Registry's original metrics() function after the last registration closes. Application-defined duplicate names still fail fast.

Duplicate metric names still fail fast

Prometheus metric names must stay unique inside a registry. Shared-registry mode keeps that behavior intact instead of silently shadowing metrics. If an application predefines a built-in HTTP collector or platform telemetry gauge name, MetricsModule.forRoot() rejects the collision instead of reusing an app-owned collector.

Runtime platform telemetry

The module emits fluo-specific gauges that mirror the platform shell and registered component state.

  • fluo_component_ready: 1 when a component is ready, otherwise 0.
  • fluo_component_health: 1 when a component is healthy, otherwise 0.
  • fluo_metrics_registry_mode: gauge value 1 with mode="isolated" when MetricsModule.forRoot() creates its registry, or mode="shared" when bootstrap supplies METRICS_REGISTRY. The label reports the effective registry ownership configuration selected during bootstrap; it does not infer registry sharing at scrape time.

The platform snapshot is refreshed during each registry scrape, including advanced MetricsService.getRegistry().metrics() scrape paths, and you can attach environment labels up front.

MetricsModule.forRoot({
  platformTelemetry: {
    env: 'production',
    instance: 'web-01',
  },
});

Runtime platform telemetry scrape contract

Platform telemetry refreshes fluo_component_ready and fluo_component_health on each active registry scrape by resolving PLATFORM_SHELL, whether the scrape flows through the built-in /metrics controller or an advanced custom scraper using MetricsService.getRegistry().

  • If PLATFORM_SHELL is not registered, the scrape still succeeds and omits the platform telemetry series.
  • If PLATFORM_SHELL becomes unavailable after the last successful scrape, stale fluo_component_ready and fluo_component_health series are removed before metrics are returned.
  • If resolving PLATFORM_SHELL fails for any other reason, the scrape surfaces that failure instead of swallowing it.

Disable default process and Node metrics

defaultMetrics defaults to true, so MetricsModule.forRoot() registers Prometheus default process and Node.js collectors once per registry unless you opt out.

MetricsModule.forRoot({
  defaultMetrics: false,
});

Public API

  • MetricsModule.forRoot(options)
  • METRICS_REGISTRY, the bootstrap-only token for an intentionally shared Registry
  • MetricsService, including counter(...), gauge(...), histogram(...), and getRegistry()
  • METER_PROVIDER
  • PrometheusMeterProvider
  • Meter abstraction types: MeterProvider, MeterCounter, MeterGauge, and MeterHistogram
  • HttpMetricsMiddleware and HTTP path-label option types
  • Module options including provider (currently only 'prometheus'), module-level middleware, and endpoint-scoped endpointMiddleware
  • Registry, re-exported from prom-client by @fluojs/metrics/integration

Operational defaults

  • path defaults to '/metrics', any string path including '' exposes a scrape endpoint, and path: false disables the scrape endpoint entirely.
  • When no bootstrap METRICS_REGISTRY provider is supplied, each application bootstrap owns a fresh isolated registry, MetricsService, meter provider, and telemetry collector set.
  • Supply METRICS_REGISTRY only through FluoFactory.create(..., { providers }); an unrelated module provider with the same token does not configure metrics ownership.
  • The scrape response uses the active registry's Prometheus content type and registry contents.
  • defaultMetrics defaults to true, and defaultMetrics: false disables Prometheus default process and Node.js collectors for that registry.
  • endpointMiddleware binds class-based route-scoped middleware only to the scrape endpoint; with HTTP instrumentation enabled, endpoint middleware failures are counted by the built-in HTTP collectors.
  • HTTP metrics are installed only when http: true or an http options object is provided, and then default to template-normalized path labels.
  • http.durationHistogramBuckets replaces the built-in HTTP request duration histogram buckets with explicit second-based boundaries.
  • Built-in HTTP collectors are reused when module instances share one registry only if they are framework-owned, have the expected label schema, and use matching HTTP instrumentation configuration; platform telemetry gauges are reused only if they are framework-owned and have the expected label schema; custom application metric name collisions keep Prometheus' duplicate-name failure behavior.
  • Shared Registry telemetry refresh remains installed while any owning metrics module is active and restores the original metrics() function after the last module closes.
  • Raw path labels require allowUnsafeRawPathLabelMode: true and should stay limited to bounded internal routes.
  • Platform telemetry is omitted only when PLATFORM_SHELL is genuinely missing; other resolution failures fail the scrape.
  • Stale platform telemetry series are removed when PLATFORM_SHELL becomes unavailable after the last successful scrape or when a later module instance refreshes a reused registry with a different platform snapshot.

Compatible duplicate copies

Compatible same-realm @fluojs/metrics copies recognize framework-owned HTTP collectors and default collectors already registered on the exact Registry. They reuse that registry-bound ownership metadata without sharing independent registries, application telemetry, or capability detection; platform capability detection remains separate from this contract. A real Registry from a compatible physical prom-client copy is accepted at the bootstrap provider boundary only after its full public Registry surface is validated before any collector or telemetry mutation; incomplete lookalikes are rejected.

Related Packages

  • @fluojs/http: contributes the request lifecycle that HTTP metrics observe
  • @fluojs/runtime: provides platform state used by runtime telemetry gauges
  • @fluojs/terminus: commonly paired with metrics for ops visibility

Example Sources

  • examples/ops-metrics-terminus/src/app.ts
  • packages/metrics/src/metrics-module.test.ts