nestjs-http-promise/lib/http.module.ts
Conicaw 021137d4d9
refactor: adjust default retries to 10
Adjust default retries to 10 to allow a longer backoff

commit-id:e43406fc
2024-03-24 00:12:44 -05:00

133 lines
3.7 KiB
TypeScript

import type { DynamicModule, Provider } from '@nestjs/common';
import { Logger, Module } from '@nestjs/common';
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import axios from 'axios';
import axiosRetry, { exponentialDelay } from 'axios-retry';
import { AXIOS_INSTANCE_TOKEN, HTTP_MODULE_ID, HTTP_MODULE_OPTIONS } from './http.constants';
import { HttpService } from './http.service';
import { isNetworkOrIdempotentRequestOrGatewayOrRateLimitError } from './http.util';
import type {
HttpModuleAsyncOptions,
HttpModuleOptions,
HttpModuleOptionsFactory,
} from './interfaces';
const createAxiosInstance = (config?: HttpModuleOptions) => {
const logger = new Logger(HttpService.name);
const axiosInstance = axios.create(config);
axiosRetry(axiosInstance, {
retries: 10,
// Default exponential backoff
retryDelay: exponentialDelay,
retryCondition: isNetworkOrIdempotentRequestOrGatewayOrRateLimitError,
onRetry(retryCount, error, requestConfig) {
logger.warn(
{
retryCount,
name: error.name,
message: error.message,
code: error.code,
method: requestConfig.method,
baseURL: requestConfig.baseURL,
url: requestConfig.url,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
params: requestConfig.params,
data: error.response?.data,
},
'Retrying request after error',
);
},
...config,
});
return axiosInstance;
};
@Module({
providers: [
HttpService,
{
provide: AXIOS_INSTANCE_TOKEN,
useValue: createAxiosInstance(),
},
],
exports: [HttpService],
})
export class HttpModule {
static register(config: HttpModuleOptions): DynamicModule {
return {
module: HttpModule,
providers: [
{
provide: AXIOS_INSTANCE_TOKEN,
useValue: createAxiosInstance(config),
},
{
provide: HTTP_MODULE_ID,
useValue: randomStringGenerator(),
},
],
};
}
static registerAsync(options: HttpModuleAsyncOptions): DynamicModule {
return {
module: HttpModule,
...(options.imports && { imports: options.imports }),
providers: [
...this.createAsyncProviders(options),
{
provide: AXIOS_INSTANCE_TOKEN,
useFactory: (config: HttpModuleOptions) => createAxiosInstance(config),
inject: [HTTP_MODULE_OPTIONS],
},
{
provide: HTTP_MODULE_ID,
useValue: randomStringGenerator(),
},
...(options.extraProviders ?? []),
],
};
}
private static createAsyncProviders(options: HttpModuleAsyncOptions): Provider[] {
if (!!options.useExisting || !!options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}
const providers = [this.createAsyncOptionsProvider(options)];
if (options.useClass) {
providers.push({
provide: options.useClass,
useClass: options.useClass,
});
}
return providers;
}
private static createAsyncOptionsProvider(options: HttpModuleAsyncOptions): Provider {
if (options.useFactory) {
return {
provide: HTTP_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject ?? [],
};
}
let inject;
if (options.useExisting) {
inject = [options.useExisting];
} else if (options.useClass) {
inject = [options.useClass];
}
return {
provide: HTTP_MODULE_OPTIONS,
useFactory: async (optionsFactory: HttpModuleOptionsFactory) =>
await optionsFactory.createHttpOptions(),
...(inject && { inject }),
};
}
}