-
-
Notifications
You must be signed in to change notification settings - Fork 109
Description
Is there an existing issue for this?
- I have searched the existing issues
Current behavior
I created custom ApiService using the following article:
https://dev.to/micalevisk/nestjs-tip-how-to-inject-multiple-versions-of-the-same-provider-into-one-module-eg-many-axios-instances-5agc
// custom http client is initialized like that:
const CUSTOM_API_SERVICE = Symbol('CUSTOM_API_SERVICE');
@Module({
imports: [
HttpModule.register({
baseURL: 'https://cats.com',
}),
],
providers: [{ provide: CUSTOM_API_SERVICE, useExisting: HttpService }],
exports: [CUSTOM_API_SERVICE],
})
export class CustomApiModule {}The problem
If CustomApiModule is initialized before TerminusModule, HealthIndicator will unintendedly retrieve the baseURL of CustomApiModule.
This happens due to getHttpService() method in HttpHealthIndicator implementation:
getHttpService() - resolves the HttpService using moduleRef.get(HttpService, { strict: false }); which crawls Nest dependency tree until it finds an instance of HttpService, the problem is that it firstly encounters the CUSTOM_API_SERVICE instance (yes even though the token itself is called CUSTOM_API_SERVICE)
I have fixed the problem in my code by providing to pingCheck a custom httpService via method options, but it does feel like an bug.
Thanks <3
Minimum reproduction code
import { Injectable, Module, OnModuleInit } from '@nestjs/common';
import { HttpModule, HttpService } from '@nestjs/axios';
import {
HealthCheckService,
HttpHealthIndicator,
TerminusModule,
} from '@nestjs/terminus';
const CUSTOM_HTTP_CLIENT_TOKEN = Symbol('A_TOKEN');
@Module({
imports: [
HttpModule.registerAsync({
useFactory: () => ({
baseURL: 'https://cats.com',
}),
}),
],
providers: [{ provide: CUSTOM_HTTP_CLIENT_TOKEN, useExisting: HttpService }],
exports: [CUSTOM_HTTP_CLIENT_TOKEN],
})
class CustomApiModule {}
@Injectable()
class HealthCheckedService implements OnModuleInit {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator
) {}
async onModuleInit() {
await this.pingHealthCheck();
}
private async pingHealthCheck() {
await this.health.check([
() => this.http.pingCheck('some-ping', 'https://dogs.com'), // debugging HealthIndicator pingCheck will show that axiosRef.defaults has baseURL of 'https://cats.com'
]);
}
}
@Module({
imports: [TerminusModule],
providers: [HealthCheckedService],
})
class HealthCheckedModule {}
@Module({
imports: [CustomApiModule],
})
class SomeModule {}
@Module({})
@Module({
imports: [SomeModule, HealthCheckedModule],
})
export class AppTest {}
// my solution
@Injectable()
class HealthCheckedService implements OnModuleInit {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
private httpService: HttpService
) {}
async onModuleInit() {
await this.pingHealthCheck();
}
private async pingHealthCheck() {
await this.health.check([
() =>
this.http.pingCheck('some-ping', 'https://dogs.com', {
httpClient: this.httpService, // fixes the problem
}),
]);
}
}Steps to reproduce
- run the code above (can import
AppTestinto AppModule) - debug health-indicator/http.health pingCheck method and look for
axiosRef.defaultswhich will show improperbaseUrl
Expected behavior
inject proper HttpService instead of trying to retrieve it using moduleRef.get(nestjs.axios.HttpService)
Package version
8.0.6
NestJS version
9.4.0
Node.js version
16.17.0
In which operating systems have you tested?
- macOS
- Windows
- Linux
Other
No response