Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injecting into middleware #1375

Closed
simplenotezy opened this issue Apr 28, 2022 · 4 comments
Closed

Injecting into middleware #1375

simplenotezy opened this issue Apr 28, 2022 · 4 comments

Comments

@simplenotezy
Copy link

simplenotezy commented Apr 28, 2022

I am trying to inject some metrics into a middleware from my AppModule.

consumer.apply(RequestMetricsMiddleware).forRoutes('*');

And this is how my middleware is defined:

import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { InjectMetric } from '@willsoto/nestjs-prometheus';
import { Counter, Histogram } from 'prom-client';

@Injectable()
export class RequestMetricsMiddleware implements NestMiddleware {
  constructor(
    @InjectMetric('node_request_operations_total') private operationsCounter: Counter<string>,
    @InjectMetric('node_request_duration_seconds') private requestDurationHistogram: Histogram<string>,
  ) {}
  use(req: Request, _: Response, next: NextFunction): void {
    var startTime = performance.now();

    req.on('close', () => {
      var endTime = performance.now();
      const totalMilliseconds = endTime - startTime;
      const name = req?.body?.query?.split('{')?.[0]?.toString().trim() || req.url;
      this.requestDurationHistogram.observe(totalMilliseconds / 1000); // convert to seconds

      Logger.debug(`Request to ${name} took ${totalMilliseconds} milliseconds`);
    });

    this.operationsCounter.inc();

    next();
  }
}

My Prometheus Module look like so:

import { makeCounterProvider, makeGaugeProvider, makeHistogramProvider, PrometheusModule } from '@willsoto/nestjs-prometheus';
import { PrometheusController } from '../PrometheusController';
import { PrometheusStatsService } from './PrometheusStats.service';

@Module({
  imports: [
    PrometheusModule.register({
      controller: PrometheusController,
    }),
  ],
  providers: [
    PrometheusStatsService,
    makeCounterProvider({
      name: 'node_request_operations_total',
      help: 'The total number of processed requests',
    }),
    makeHistogramProvider({
      name: 'node_request_duration_seconds',
      help: 'Histogram for the request duration in seconds',
      buckets: [0, 1, 2, 5, 6, 10],
    }),
  ],
  exports: [PrometheusStatsService],
})
export class PrometheusStatsModule {}

Which I import in my AppModule under the imports array.

I am getting the error:

This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:

Error: Nest can't resolve dependencies of the class RequestMetricsMiddleware {
    constructor(operationsCounter, requestDurationHistogram) {
        this.operationsCounter = operationsCounter;
        this.requestDurationHistogram = requestDurationHistogram;
    }
    use(req, _, next) {
        var startTime = performance.now();
        req.on('close', () => {
            var _a, _b, _c, _d;
            var endTime = performance.now();
            const totalMilliseconds = endTime - startTime;
            const name = ((_d = (_c = (_b = (_a = req === null || req === void 0 ? void 0 : req.body) === null || _a === void 0 ? void 0 : _a.query) === null || _b === void 0 ? void 0 : _b.split('{')) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.toString().trim()) || req.url;
            this.requestDurationHistogram.observe(totalMilliseconds / 1000);
            common_1.Logger.debug(`Request to ${name} took ${totalMilliseconds} milliseconds`);
        });
        this.operationsCounter.inc();
        next();
    }
} (?, PROM_METRIC_NODE_REQUEST_DURATION_SECONDS). Please make sure that the argument PROM_METRIC_NODE_REQUEST_OPERATIONS_TOTAL at index [0] is available in the AppModule context.

Potential solutions:
- If PROM_METRIC_NODE_REQUEST_OPERATIONS_TOTAL is a provider, is it part of the current AppModule?
- If PROM_METRIC_NODE_REQUEST_OPERATIONS_TOTAL is exported from a separate @Module, is that module imported within AppModule?
  @Module({
    imports: [ /* the Module containing PROM_METRIC_NODE_REQUEST_OPERATIONS_TOTAL */ ]
  })

    at Injector.lookupComponentInParentModules (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:231:19)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at Injector.resolveComponentInstance (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:184:33)
    at resolveParam (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:106:38)
    at async Promise.all (index 0)
    at Injector.resolveConstructorParams (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:121:27)
    at Injector.loadInstance (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:52:9)
    at Injector.loadMiddleware (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/injector/injector.js:61:9)
    at MiddlewareResolver.resolveMiddlewareInstance (/Users/mf/Projects/a/backend/node_modules/@nestjs/core/middleware/resolver.js:16:9)
    at async Promise.all (index 0)

Update

Temporarily solved the issue by adding the makeCounterProvider and makeHistogramProvider to exports as well, although not very pretty/DRY.

@willsoto
Copy link
Owner

Your solution makes total sense and is expected. In order for the any injectable to be visible to other modules, they must be exported unless they all live within that module. The Nest.js docs explain these concepts

@simplenotezy
Copy link
Author

I see @willsoto I just didn't really like repeating the code twice, so perhaps there was another solution 😊

@willsoto
Copy link
Owner

willsoto commented Apr 28, 2022

You can always make them a variable if you want. Nothing that says they have to be declared inline in the providers array.

@apgapg
Copy link

apgapg commented Nov 13, 2022

I think it will be better if we update DOCS as this package is specific to NESTJS.
I shall raise PR @willsoto for your last comment too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants