NestJS HTTP Promise Module with Retries
nestjs-http-promise is a NestJS module that extends the framework's official HTTP capabilities by providing a promise-based API for Axios-powered requests. This library, currently at version 4.0.0, aims to simplify HTTP client interactions by eliminating the need for explicit `.toPromise()` calls on RxJS Observables, which are typically returned by NestJS's default HttpModule. A key differentiator is its out-of-the-box integration of automatic request retries using `axios-retry` and enhanced Axios stack traces for improved debugging. The package generally follows major NestJS and Axios version updates, ensuring compatibility with the latest ecosystem features. It offers both static and asynchronous configuration options, allowing for flexible setup within various NestJS architectural patterns. Its primary function is to provide a more imperative, promise-centric approach to HTTP requests within a declarative NestJS module structure, contrasting with the Observable-first approach of the base NestJS HTTP module.
Common errors
-
Nest can't resolve dependencies of the HttpService (?). Please make sure that the argument at index [0] is available in the ApiIntegrationModule context.
cause HttpModule was not properly imported or configured in the module where HttpService is being injected.fixVerify that `HttpModule` (either `HttpModule.register()` or `HttpModule.registerAsync()`) is correctly listed in the `imports` array of the module that declares the component injecting `HttpService`. -
Error: Cannot find module 'nestjs-http-promise'
cause The package was not installed or an incorrect import path was used.fixRun `npm install nestjs-http-promise` or `yarn add nestjs-http-promise`. Check that the import statement matches `import { ... } from 'nestjs-http-promise'`. -
TypeError: Cannot read properties of undefined (reading 'get') (or similar for post, put, etc.) on HttpService instance.
cause HttpService was injected but its dependencies (Axios) might not have been properly initialized, or the module was not set up correctly leading to an undefined HttpService.fixEnsure `HttpModule` is correctly imported in the module that provides `HttpService`. For `registerAsync`, double-check the `useFactory` or `useClass` implementation to ensure valid `HttpModuleOptions` are returned.
Warnings
- breaking Version 3.0.0 introduced significant dependency upgrades, bumping Axios from v0.x.x to v1.x.x and NestJS peer dependency to v10.x.x. This may require manual updates to your project's `axios` and `@nestjs/*` dependencies to maintain compatibility.
- gotcha The `isBetterStackTraceEnabled` feature, which adds data to Axios stack traces, is enabled by default. While generally helpful, it can be explicitly disabled by setting `isBetterStackTraceEnabled: false` in the module configuration if it causes unexpected behavior or performance overhead in specific environments.
- gotcha When using `HttpModule.registerAsync()` with `useClass`, the implementing class (e.g., `HttpConfigService`) must implement the `HttpModuleOptionsFactory` interface and must also be provided in the `providers` array of the importing module. Failure to provide the class will result in a NestJS DI error.
Install
-
npm install nestjs-http-promise -
yarn add nestjs-http-promise -
pnpm add nestjs-http-promise
Imports
- HttpModule
const { HttpModule } = require('nestjs-http-promise')import { HttpModule } from 'nestjs-http-promise' - HttpService
import HttpService from 'nestjs-http-promise'
import { HttpService } from 'nestjs-http-promise' - HttpModuleOptionsFactory
import { IHttpModuleOptionsFactory } from 'nestjs-http-promise'import { HttpModuleOptionsFactory } from 'nestjs-http-promise'
Quickstart
import { Module, Injectable, INestApplication } from '@nestjs/common';
import { HttpModule, HttpService, HttpModuleOptionsFactory, HttpModuleOptions } from 'nestjs-http-promise';
import { NestFactory } from '@nestjs/core';
// Example of an async configuration service
@Injectable()
class HttpConfigService implements HttpModuleOptionsFactory {
async createHttpOptions(): Promise<HttpModuleOptions> {
// Simulate fetching configuration data asynchronously, e.g., from a config service or environment variables
const configurationData = await Promise.resolve({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 5000,
maxRetries: 3,
isBetterStackTraceEnabled: true // Enable improved stack traces by default
});
return {
baseURL: configurationData.baseURL,
timeout: configurationData.timeout,
retries: configurationData.maxRetries,
isBetterStackTraceEnabled: configurationData.isBetterStackTraceEnabled,
};
}
}
@Injectable()
class MyApiService {
constructor(private readonly httpService: HttpService) {}
/**
* Fetches a post by ID. This call benefits from the module's configured retries.
*/
async fetchPost(id: number): Promise<any> {
console.log(`Attempting to fetch post ${id}...`);
// The HttpService methods return Promises directly
const response = await this.httpService.get(`/posts/${id}`);
return response.data;
}
/**
* Creates a new post. Retries can be overridden per request.
*/
async createPost(title: string, body: string): Promise<any> {
console.log('Attempting to create a new post...');
const response = await this.httpService.post('/posts', { title, body, userId: 1 }, {
retries: 0 // No retries for create operations for this specific request
});
return response.data;
}
}
@Module({
imports: [
// Asynchronously configure HttpModule using a factory class
HttpModule.registerAsync({
useClass: HttpConfigService,
}),
],
providers: [MyApiService, HttpConfigService], // HttpConfigService must be provided if used with useClass
exports: [MyApiService], // Export the service if it's used by other modules
})
class ApiIntegrationModule {}
async function bootstrap() {
const app: INestApplication = await NestFactory.create(ApiIntegrationModule);
await app.listen(3000, () => {
console.log('NestJS Application listening on port 3000');
});
const apiService = app.get(MyApiService);
try {
const post = await apiService.fetchPost(1);
console.log('\nFetched Post 1:', post);
const newPost = await apiService.createPost('Hello Registry', 'This is a test post from the registry quickstart.');
console.log('\nCreated New Post:', newPost);
} catch (error: any) {
console.error('\nAn error occurred during API calls:');
console.error('Error message:', error.message);
if (error.response) {
console.error('Response status:', error.response.status);
console.error('Response data:', error.response.data);
} else if (error.code === 'ECONNABORTED') {
console.error('Request timed out or cancelled.');
}
} finally {
await app.close();
console.log('\nApplication closed.');
}
}
// To run this quickstart, save it as a .ts file in a NestJS project and execute with `ts-node` or compile.
// In a real application, `bootstrap()` would be called from `main.ts`.
bootstrap();