Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Test

on:
pull_request:
branches:
- master
types: [opened, synchronize, reopened]

jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
matrix:
version:
- 18
- 20
- 22
steps:
- name: Check out repository and branch
uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.version }}

- name: Install
run: npm ci

- name: Test
run: npm run test
267 changes: 187 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,125 +1,232 @@
# nestjs-http-promise
# `nestjs-http-promise`

[![npm version](https://img.shields.io/npm/v/nestjs-http-promise.svg?style=flat-square)](https://www.npmjs.org/package/nestjs-http-promise)
[![npm downloads](https://img.shields.io/npm/dm/nestjs-http-promise.svg?style=flat-square)](http://npm-stat.com/charts.html?package=nestjs-http-promise)

## description
nestjs module that just doing little modification to the original and good **nestjs** http module.


## features
* axios - the most used package for http requests in npm and the one used by nestjs official http library.
* better axios stack trace - axios has an [open issue](https://github.com/axios/axios/issues/2387) about improvement of their stack trace.
in this library there is a default interceptor that will intercept the stack trace and will add data to it.
* promise based - most of us using the current http module that uses observable which we don't use most of the time
and in order to avoid it were just calling `.toPromise()` every http call.
* retries - in many cases we will want to retry a failing http call.
with observable we could just add the retry operator (rxjs) but with promises we need to implement this logic ourselves.
this package will make it easy for you, just pass `{ retries: NUMBER_OF_RETRIES }` in the config of the http module.
**more details in the configuration section**

## quick start
### installing
A promise-based implementation of `@nestjs/axios` with retries without needing
to mess around with `rxjs`'s `Observable` data structures.

## Features

- Axios
- The most used package for HTTP requests in npm and the one used by NestJS's
official HTTP library.
- Promise-based
- Most uses of `@nestjs/axios` just call `.toPromise()` on every HTTP call to
exit out of the `rxjs` `Observable` data structure without leveraging it
any further.
- Retries
- Leverages `axios-retry` to provide retries out of box with promises.

## Quick start

### Installing

Using npm:

```
$ npm install nestjs-http-promise
```

Using yarn:

```
$ yarn add nestjs-http-promise
```

### usage - just like every nest.js module
import the module:
```ts
import { HttpModule } from 'nestjs-http-promise'
### Usage - just like every nest.js module

Import the module:

@Module({
imports: [HttpModule]
```ts
import { Module } from '@nestjs/common';
import { HttpModule } from 'nestjs-http-promise';

@Module({
imports: [HttpModule],
provide: [MyService],
})
export class MyModule {}
```

inject the service in the class:
Inject the service in the class and use it:

```ts
import { HttpService } from 'nestjs-http-promise'
import { Injectable } from '@nestjs/common';
import { HttpService } from 'nestjs-http-promise';

@Injectable()
class MyService {
constructor(private readonly httpService: HttpService) {}

class Demo {
constructor(private readonly httpService: HttpService) {}
public callSomeServer(): Promise<object> {
return this.httpService.get('http://fakeService');
}
}
```

use the service:
## Configuration

The service uses `axios` and `axios-retry` and their configurations can be
provided via the module's options via `HttpModule.register` or
`HttpModule.registerAsync` just like with all other NestJS modules.

### Synchronous register example

```ts
public callSomeServer(): Promise<object> {
return this.httpService.get('http://fakeService')
}
```
import { Module } from '@nestjs/common';
import { HttpModule } from 'nestjs-http-promise';

## configuration
@Module({
imports: [
HttpModule.register({
axiosConfig: {
// AxiosRequestConfig same as you would pass to `Axios.create({ ... })`
// https://github.com/axios/axios#request-config
},
axiosRetryConfig: {
// IAxiosRetryConfig same as you would pass to `axiosRetry(axios, { ... })`
// https://github.com/softonic/axios-retry#options
},
interceptors: {
// See interceptors documentation below
},
}),
],
})
export class MyModule {}
```

the service uses axios and axios-retry, so you can pass any [AxiosRequestConfig](https://github.com/axios/axios#request-config)
And/Or [AxiosRetryConfig](https://github.com/softonic/axios-retry#options)
### Asynchronous register example

just pass it in the `.register()` method as you would do in the original nestjs httpModule
```ts
import { HttpModule } from 'nestjs-http-promise'
import { Module } from '@nestjs/common';
import { HttpModule } from 'nestjs-http-promise';

@Module({
imports: [HttpModule.register(
{
timeout: 1000,
retries: 5,
...
}
)]
imports: [
HttpModule.registerAsync({
inject: ['SOMETHING_FROM_GLOBAL_FOR_EXAMPLE'],
useFactory: (somethingGlobalForExample: Record<string, string>) => ({
axiosConfig: {
// AxiosRequestConfig same as you would pass to `Axios.create({ ... })`
},
axiosRetryConfig: {
// IAxiosRetryConfig same as you would pass to `axiosRetry(axios, { ... })`
retries: Number(somethingGlobalForExample.retries),
},
interceptors: {
// See interceptors documentation below
},
}),
}),
],
})
export class MyModule {}
```

### default configuration
* default config of axios-retry : https://github.com/softonic/axios-retry#options
* better axios stack trace is added by default, you can turn it off by passing the **isBetterStackTraceEnabled** to false.
Refer to NestJS's documentation for more advanced async module instantiation.

## async configuration
When you need to pass module options asynchronously instead of statically, use the `registerAsync()` method **just like in nest httpModule**.
### Registering Axios interceptors

This module supports optionally configuring Axios interceptors via module
configuration.

you have a couple of techniques to do it:
* with the useFactory
```ts
HttpModule.registerAsync({
useFactory: () => ({
timeout: 1000,
retries: 5,
...
import { Module } from '@nestjs/common';
import { HttpModule } from 'nestjs-http-promise';

@Module({
imports: [
HttpModule.register({
interceptors: {
request: {
onFulfilled: (requestConfig: InternalAxiosRequestConfig) => {
// Custom logic here

return requestConfig;
},
onRejected: (error: any) => {
// Custom logic here

return error;
},
options: {},
},

response: {
onFulfilled: (response: AxiosResponse) => {
// Custom logic here

return response;
},
onRejected: (error: any) => {
// Custom logic here

return error;
},
options: {},
},
},
}),
});
],
})
export class MyModule {}
```

* using class
The equivalent implementation when using Axios directly would be this:

```ts
HttpModule.registerAsync({
useClass: HttpConfigService,
});
import { Axios } from 'axios';

const client = Axios.create();

client.interceptors.request.use(
(requestConfig: InternalAxiosRequestConfig) => {
// Custom logic here

return requestConfig;
},
(error: any) => {
// Custom logic here

return error;
},
{},
);

client.interceptors.response.use(
(response: AxiosResponse) => {
// Custom logic here

return response;
},
(error: any) => {
// Custom logic here

return error;
},
{},
);
```
Note that in this example, the HttpConfigService has to implement HttpModuleOptionsFactory interface as shown below.

You can also interact with the Axios interceptors after construction via the
`axiosRef` on the HTTP service:

```ts
import { Injectable } from '@nestjs/common';
import { HttpService } from 'nestjs-http-promise';

@Injectable()
class HttpConfigService implements HttpModuleOptionsFactory {
async createHttpOptions(): Promise<HttpModuleOptions> {
const configurationData = await someAsyncMethod();
return {
timeout: configurationData.timeout,
retries: 5,
...
};
class MyService {
constructor(private readonly httpService: HttpService) {}

public doThing() {
this.httpService.axiosRef.interceptors.request.use(requestConfig => {
// Custom logic here

return requestConfig;
});
}
}
```
If you want to reuse an existing options provider instead of creating a copy inside the HttpModule,
use the useExisting syntax.
```ts
HttpModule.registerAsync({
imports: [ConfigModule],
useExisting: ConfigService,
});
```
4 changes: 2 additions & 2 deletions lib/http.constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const AXIOS_INSTANCE_TOKEN = 'AXIOS_INSTANCE_TOKEN';
export const HTTP_MODULE_ID = 'HTTP_MODULE_ID';
export const HTTP_MODULE_OPTIONS = 'HTTP_MODULE_OPTIONS';
export const HTTP_MODULE_ID_TOKEN = 'HTTP_MODULE_ID';

Loading