node-prometheus
    Preparing search index...

    node-prometheus

    πŸ“Š @sourceregistry/node-prometheus

    A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus & OpenMetrics-compatible metrics.

    License: Apache 2.0 npm version

    Supports:

    • βœ… Counter
    • βœ… Gauge
    • βœ… Histogram
    • βœ… Summary
    • βœ… Untyped

    Exports metrics in Prometheus exposition format and OpenMetrics v1.0.0 β€” ready for modern observability stacks.


    npm install @sourceregistry/node-prometheus
    

    βœ… Zero external dependencies β€” pure TypeScript.


    import { Counter } from '@sourceregistry/node-prometheus';

    let requestCount = 0;

    const counter = new Counter({
    name: 'http_requests_total',
    description: 'Total number of HTTP requests',
    reader: () => [requestCount]
    });

    // Later...
    requestCount++;
    import { Gauge } from '@sourceregistry/node-prometheus';

    const gauge = new Gauge({
    name: 'current_temperature_celsius',
    description: 'Current temperature in Celsius',
    reader: () => [getTemperature()] // e.g., returns 23.5
    });
    import { Histogram } from '@sourceregistry/node-prometheus';

    const histogram = new Histogram({
    name: 'response_time_seconds',
    description: 'HTTP response time in seconds',
    buckets: [0.1, 0.5, 1, 2.5, 5]
    });

    histogram.observe(0.73); // Updates buckets, sum, and count
    import { Summary } from '@sourceregistry/node-prometheus';

    const summary = new Summary({
    name: 'request_duration_seconds',
    description: 'Request duration with quantiles',
    quantiles: [
    { quantile: 0.5, error: 0.05 },
    { quantile: 0.9, error: 0.01 },
    { quantile: 0.99, error: 0.001 }
    ]
    });

    summary.observe(1.2);
    summary.observe(0.8);
    import { Untyped } from '@sourceregistry/node-prometheus';

    const untyped = new Untyped({
    name: 'some_legacy_metric',
    value: 42
    });

    untyped.set(43);

    Use Metric.concat() to serialize multiple metrics into a single string. You can specify 'prometheus' (default) or 'openmetrics' format.

    import { Metric, Counter, Gauge } from '@sourceregistry/node-prometheus';

    let requestCount = 0;

    const counter = new Counter({
    name: 'http_requests_total',
    description: 'Total HTTP requests',
    reader: () => [requestCount]
    });

    const gauge = new Gauge({
    name: 'cpu_usage_percent',
    description: 'Current CPU usage',
    reader: () => [Math.random() * 100]
    });

    // Serialize in Prometheus format (default)
    const promOutput = await Metric.concat('prometheus', counter, gauge);
    console.log(promOutput);
    // # HELP http_requests_total ...
    // # TYPE http_requests_total counter
    // http_requests_total 0 1712345678901
    // ...

    // Serialize in OpenMetrics format
    const omOutput = await Metric.concat('openmetrics', counter, gauge);
    console.log(omOutput);
    // # HELP http_requests_total ...
    // # TYPE http_requests_total counter
    // http_requests_total 0 1712345678901
    // ...
    // # EOF

    βœ… In OpenMetrics mode, # EOF is automatically appended, and trailing whitespace is trimmed.


    This example uses Metric.concat(format, ...) to serve the correct format based on the client’s Accept header.

    // examples/server.ts
    import { createServer } from 'http';
    import { Counter, Gauge, Histogram, Metric, Untyped } from '@sourceregistry/node-prometheus';

    const gauge = new Gauge({
    name: 'random_gauge',
    description: 'Random gauge value updated on each scrape',
    reader: () => [Math.random() * 100],
    });

    const histogram = new Histogram({
    name: 'random_histogram',
    description: 'Histogram of random values',
    buckets: [0.1, 0.2, 0.5, 1.0],
    });

    let hits = 0;

    const counter = new Counter({
    name: 'http_requests_total',
    description: 'Total HTTP requests handled',
    reader: () => [[hits, { method: 'GET', endpoint: '/metrics' }]],
    });

    const untyped = new Untyped({
    name: 'uptime_seconds',
    description: 'Server uptime in seconds',
    value: [0, Date.now()],
    });

    // Simulate background metric updates
    setInterval(() => {
    histogram.observe(Math.random());
    const uptime = (Date.now() - untyped.get()[1]) / 1000;
    untyped.set(uptime);
    }, 2000);

    // HTTP Server with OpenMetrics negotiation
    createServer(async (req, res) => {
    console.log(`Scraped at ${new Date().toISOString()}`);
    hits++;

    const acceptHeader = req.headers['accept'] || '';
    const format = acceptHeader.includes('application/openmetrics-text') ? 'openmetrics' : 'prometheus';

    res.writeHead(200, {
    'Content-Type': format === 'openmetrics'
    ? 'application/openmetrics-text; version=1.0.0; charset=utf-8'
    : 'text/plain; version=0.0.4; charset=utf-8',
    });

    const output = await Metric.concat(format, gauge, histogram, counter, untyped);
    res.end(output);
    }).listen(8080, '0.0.0.0', () => {
    console.log('βœ… Metrics server: http://localhost:8080');
    });

    Run it:

    npm run example::http.server
    

    Then test:

    curl -H "Accept: application/openmetrics-text" http://localhost:8080
    curl http://localhost:8080 # defaults to Prometheus format

    Compatible with classic Prometheus scrapers:

    # HELP http_requests_total Total number of HTTP requests
    # TYPE http_requests_total counter
    http_requests_total{method="GET",endpoint="/metrics"} 5 1712345678901

    # HELP response_time_seconds HTTP response time in seconds
    # TYPE response_time_seconds histogram
    response_time_seconds_bucket{le="0.1"} 3
    response_time_seconds_bucket{le="0.5"} 15
    response_time_seconds_bucket{le="1"} 20
    response_time_seconds_bucket{le="+Inf"} 20
    response_time_seconds_sum 12.34
    response_time_seconds_count 20

    When client sends Accept: application/openmetrics-text, server responds with:

    # HELP random_gauge Random gauge value updated on each scrape
    # TYPE random_gauge gauge
    random_gauge 42.17 1712345678901

    # HELP random_histogram Histogram of random values
    # TYPE random_histogram histogram
    random_histogram_bucket{le="0.1"} 3 1712345678901
    random_histogram_bucket{le="0.2"} 7 1712345678901
    random_histogram_bucket{le="0.5"} 15 1712345678901
    random_histogram_bucket{le="1.0"} 20 1712345678901
    random_histogram_bucket{le="+Inf"} 20 1712345678901
    random_histogram_sum 12.34 1712345678901
    random_histogram_count 20 1712345678901

    # HELP http_requests_total Total HTTP requests handled
    # TYPE http_requests_total counter
    http_requests_total{method="GET",endpoint="/metrics"} 5 1712345678901

    # HELP uptime_seconds Server uptime in seconds
    # TYPE uptime_seconds untyped
    uptime_seconds 124.3 1712345678901

    # EOF

    βœ… Required: +Inf bucket, # EOF, correct Content-Type.


    • βœ… Type-safe β€” Full TypeScript support with JSDoc.
    • βœ… Async/Sync Readers β€” Gauge/Counter support both.
    • βœ… Label Support β€” Per-metric and per-sample labels.
    • βœ… Timestamps β€” Optional sample timestamps.
    • βœ… Validation β€” Input sanitization and error handling.
    • βœ… Extensible β€” Easy to extend or override behavior.
    • βœ… OpenMetrics Ready β€” Auto-negotiation support in HTTP example.
    • βœ… Zero Dependencies β€” Pure TypeScript, no bloat.

    Comes with Vitest-compatible tests covering all metric types, serialization, edge cases, and error handling.

    Run tests:

    npm test          # Watch mode
    npm run test:ui # GUI
    npm run test:coverage # Coverage report

    See the examples/ folder in this repository for:

    • Basic metric usage
    • HTTP server with Prometheus/OpenMetrics support
    • Histogram/Summary simulations

    Apache License 2.0 β€” See LICENSE for details.


    PRs welcome! Please ensure:

    • βœ… Code matches existing style and JSDoc standards
    • βœ… Tests are added for new features
    • βœ… No external dependencies added
    • βœ… npm run build and npm test pass

    πŸ’‘ Note: This library generates exposition format only. You must expose it via HTTP (e.g., Express, Fastify, or plain http) for Prometheus to scrape. See the HTTP example above to get started quickly.