Injecting into middleware
Closed this issue ยท 4 comments
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.
I see @willsoto I just didn't really like repeating the code twice, so perhaps there was another solution ๐
You can always make them a variable if you want. Nothing that says they have to be declared inline in the providers
array.