简体   繁体   中英

NestJS HttpService All Method API Call is not working when Interceptor is used

NestJS API App use HttpService to call Another API and It works when no custom interceptor is used. The HttpService API call is executed but it is not reached to another API and could not see the response.

This is get call code


 get(path: string, body: any = {}): Observable<AxiosResponse<any>> {
    console.log('DomainAPI Response Begining');
    const url = this.baseURL + path;
    this.updateLastUtilizedTimestamp();
    return this.httpService.get(url, { validateStatus: null }).pipe(
      tap(data => {
        console.log('DomainAPI Response Tap', data);
      }),
      retryWhen(
        this.rxJSUtilsService.genericRetryStrategy({
          numberOfAttempts: 3,
          delayTime: 200,
          ignoredErrorCodes: [500],
        }),
      ),
      catchError(this.formatErrors),
    );
  }

if any custom interceptor is used, I found the following when debug.

arguments:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

The Postman shows the following response.

{
    "statusCode": 500,
    "message": {
        "Title": "TypeError",
        "Type": "Error",
        "Detail": "Converting circular structure to JSON\n    --> starting at object with constructor 'Observable'\n    |     property 'operator' -> object with constructor 'CatchOperator'\n    --- property 'caught' closes the circle",
        "Status": "Status",
        "Extension": ""
    },
    "timestamp": "Exception - AllExceptionsFilter 2019-12-26T17:29:42.447Z"
}

I changed the above as below but still it is not working


get(path: string, body: any = {}) {
    console.log('DomainAPI Response Begining');
    const url = this.baseURL + path;
    this.updateLastUtilizedTimestamp();
    return this.httpService.get(url, { validateStatus: null }).pipe(
      map(response => {
        console.log('DomainAPI Response Tap', response.data);
        return response.data;
      }),
    );
  }

It gives the following response in the postman


{
    "data": {
        "_isScalar": false,
        "source": {
            "_isScalar": false,
            "source": {
                "_isScalar": false
            },
            "operator": {}
        },
        "operator": {}
    }
}

Please advise

async get(path: string, body: any = {}): Promise<any> {
    ...
    const res = await this.httpService.get(url, { validateStatus: null }).toPromise();

    return res.data;
  }
get(path: string, body: any = {}) {
   ...
    return this.httpService.get(url, { validateStatus: null })
    .toPromise()
    .then(res => res.data)
    .catch(err => this.logger.error(err));
  }

Converting to a promise using the.toPromise() method has been deprecated because we are working with observables and a promise returns only one value while observables could return no value or more that one value (see more @: Conversion to Promises ). So as an example using the question above the solution would be:

import { lastValueFrom } from 'rxjs';

get(path: string, body: any = {}): Observable<AxiosResponse<any>> {
    console.log('DomainAPI Response Begining');
    const url = this.baseURL + path;
    this.updateLastUtilizedTimestamp();
 
    return await lastValueFrom(
      this.httpService.get(url, { validateStatus: null })
      .pipe(
      tap(data => {
        console.log('DomainAPI Response Tap', data);
      }),
      retryWhen(
        this.rxJSUtilsService.genericRetryStrategy({
          numberOfAttempts: 3,
          delayTime: 200,
          ignoredErrorCodes: [500],
        }),
      ),
      catchError(this.formatErrors),
    ));
  }

Basically all you have to do is wrap the whole http request with firstValueFrom or lastValueFrom, in this case lastValueFrom is what would be appropriate. Visit the link to the documentation on Conversion to promises above by rxjs to know more about this.

NOTE: I added just the lastValueFrom import only as a clear indicator of what is needed.

This is another example using my own code:

async send_otp(
    phone: string,
    channel: string,
    ): Promise<Observable<AxiosResponse<any>>> {
    return await lastValueFrom(
      this.httpService
        .post(`/messaging/otp/`, {
          sender: 'Oyinkayy',
          destination: '234' + String(phone).slice(1),
          length: 6,
          channel: channel,
          reference: '',
        })
        .pipe(
          tap((resp) => console.log(resp)),
          map((resp) => {
            return resp.data;
          }),
          tap((data) => console.log(data)),
          catchError(async (err) => {
            return err;
          }),
        ),
    );
   }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM