Reputation: 11
I have a multi module Nest application in which I use the HttpModule
to perform HTTP requests.
In one of my services, I send requests to a third-party application that allows a maximum of 3 requests per second. Therefore, I want to add a request interceptor to the Axios instance used in this service. Since modules in Nest are singletons, they share the same instance of providers, and hence the same Axios instance of the HttpService
. Therefore, if I modify the Axios instance of the injected HttpService
in one of my providers and e.g. add a request interceptor (httpService.axiosRef.interceptors.request.use(...)
), each request that is send from any service within my application, that uses the injected HttpService
, will be intercepted.
Exemplary module structure of the application:
// app.module.ts
@Module({
imports: [
ExternalIntegrationAPIModule,
ExternalManagementAPIModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
// externalIntegrationAPI.module.ts
@Module({
imports: [HttpModule],
exports: [ExternalIntegrationAPIConnector],
providers: [ExternalIntegrationAPIConnector],
})
export class ExternalIntegrationAPIModule {}
// externalIntegrationAPI.connector.ts
@Injectable()
export class ExternalIntegrationAPIConnector {
constructor(
private httpService: HttpService,
) {...}
// ...methods that use httpService to perform requests
}
// externalManagementAPI.module.ts
@Module({
imports: [HttpModule],
exports: [ExternalManagementAPIConnector],
providers: [ExternalManagementAPIConnector],
})
export class ExternalManagementAPIModule {}
// externalManagementAPI.connector.ts
@Injectable()
export class ExternalManagementAPIConnector {
constructor(
private httpService: HttpService,
) {
// this.httpService.axiosRef.interceptors.request.use(...);
// Since both, ExternalManagementAPIConnector and ExternalIntegrationAPIConnector
// share the same Axios instance this would also intercept requests made from
// ExternalIntegrationAPIConnector
}
// ...methods that use httpService to perform requests
}
In this case I want to intercept the requests made from ExternalManagementAPIConnector
to ensure the limit of 3 requests per second while avoiding to also intercept any requests performed from ExternalIntegrationAPIConnector
.
How can I add an interceptor such that only the requests performed in the desired service are intercepted?
I tried using the register
function of the HttpModule
to create an independent Axios instance. Furthermore, I tried to create my own extension of the HttpModule
and HttpService
to create an idependent Axios instance. That works as long as I only import this new module once. Every other module that imports this module will again share the same Axios instance, similar to the behaviour with the original HttpModule
Update:
I tried using the solution proposed here: NestJS: New instance of HttpModule per module import.
However, at first this also intercepted the health pings from the TerminusModule
. This could be solved by also injecting the HttpService
in my health service and passing the injected HttpService as config:
@Injectable()
class HealthService {
constructor(
private readonly healthCheckService: HealthCheckService,
private readonly httpHealthIndicator: HttpHealthIndicator,
private readonly httpService: HttpService,
...
) {...}
async checkHealth() {
const healthCheck = await this.healthCheckService.check([
() => this.httpHealthIndicator.pingCheck(
'targetKeyName',
{ httpClient: this.httpService }
),
...
]);
}
}
Since calling register()
method with an empty config object in the import to create a separate instance of the HttpModule
seems like a hacky solution, I still would be curious if there are any other clean approaches to my issue.
Upvotes: 1
Views: 3655
Reputation: 997
What about defining a custom provider for the HTTP service and configuring the interceptor within the provider?
// externalManagementAPI.module.ts
import { HttpModule, HttpService, Module } from '@nestjs/common';
import { AxiosRequestConfig } from 'axios';
@Module({
imports: [HttpModule],
exports: [ExternalManagementAPIConnector],
providers: [
ExternalManagementAPIConnector,
{
provide: 'HttpServiceWithInterceptor',
useFactory: (httpService: HttpService) => {
const axiosInstance = httpService.axiosRef;
axiosInstance.interceptors.request.use((config:
AxiosRequestConfig) => {
// interceptor logic
// You can modify the request config or perform
return config;
});
return axiosInstance;
},
inject: [HttpService],
},
],
})
export class ExternalManagementAPIModule { }
// externalManagementAPI.connector.ts
import { HttpService, Inject, Injectable } from '@nestjs/common';
import { AxiosInstance } from 'axios';
@Injectable()
export class ExternalManagementAPIConnector {
constructor(
@Inject('HttpServiceWithInterceptor')
private httpService: AxiosInstance,
) {
// You can use this.httpService to make requests with the interceptor applied
}
// methods that use httpService to perform requests
}
Upvotes: 1