WebDev

NestJS: HttpService retry configuration

Daniel Ortega

Daniel Ortega

Software Engineer

NestJS Logo
In this post, I’ll walk you through a practical and clean way to configure retry logic for HTTP requests in NestJS using the HttpModule. The goal is to keep things simple, readable, and production-ready.
If you want to go deeper, the official NestJS documentation explains the HttpModule in detail. For our purposes, it’s enough to know that it is essentially a thin wrapper around Axios, exposing requests as RxJS Observables.
Installing dependencies
First, we need to install the required packages:
$npm i --save @nestjs/axios axios
Importing the HttpModule
Next, we import HttpModule into the module where we plan to make HTTP calls.
@Module({
controllers: [CarsController],
imports: [PrismaModule, HttpModule],
providers: [
{
provide: 'ICarsRepository',
useClass: CarsRepository,
}
CarsService,
Logger,
],
exports: [CarsService, CarsRepository],
}
export class CarsModule {}
Once imported, HttpService can be injected into any provider.
@Injectable()
export class CarsService {
constructor(private readonly httpService: HttpService) {}
public findAll() {
return this.httpService.get('http://localhost:3000/cars');
}
}
The request is very basic and has no retry logic. Let’s improve that.
Defining a custom error
class CarsApiError extends Error {
constructor(message: string, public readonly status: number) {
super(message);
}
}
Adding retry logic with RxJS
const response = this.httpService
.get('http://localhost:3000/cars')
.pipe(
map((response) => {
if (response.status === HttpStatus.OK) {
return response.data;
}
throw new CarsApiError('Cars API error', response.status);
}),
retry({
count: retryCount,
delay: (error, retryAttempt) => {
if (error instanceof CarsApiError && error.status >= HttpStatus.INTERNAL_SERVER_ERROR) {
this.logger.warn(`Retrying Cars API. Attempt: ${retryAttempt}`);
return timer(retryDelay);
}
throw error;
}
}),
catchError((error) => {
this.logger.error(error);
throw error;
}),
);
Retry behavior is configurable using ConfigService.
const retryCount = Number(this.config.get('HTTP_SERVICE_RETRY_COUNT'));
const retryDelay = Number(this.config.get('HTTP_SERVICE_RETRY_DELAY'));
Finally, convert the observable into a promise using lastValueFrom.
const carValues = await lastValueFrom(response);
return Car.toEntity(...carValues);
With this setup, retries are explicit, configurable, and limited to the errors we actually care about.