Phate
Phate

Reputation: 6612

How to process axios httpservice observable response?

I think I'm getting crazy as I'm pretty new to node and typescript...I simply want to retrieve, in a syncronous way, the result of an http get request.

Given:

import { Injectable, HttpService } from '@nestjs/common';
import {} from '@nestjs/core';

@Injectable()
export class AppService {
  private readonly DATA_URL:string = "https://remote/data.json";
  constructor(private httpService:HttpService){}
  getSomething(): Array<Object> {
   let resp = this.httpService.get(this.DATA_URL); //what do I do now?? It's an observable
  }
}

edit: I'm writing here the full code as it could be useful to others learning the framework. I used Jay's response, but richbai also helped me a lot in understanding the theory behind. Of course improve/correct if it can still get better.

  1. I added a type to have better control instead of Object
  2. I needed to change the date field from the response from "yyyy-mm-ddThh24:mi:ss" to "yyyy-mm-dd"
  3. I also needed to filter the response based on a value

     getSomething(aFilterValue:number): Observable<RespDTO[]> {
        return this.httpService.get(this.DATA_URL).pipe(
        map((axiosResponse : AxiosResponse) => (axiosResponse.data as 
       RespDTO[])
    .filter((el:RespDTO) => el.aCode===aFilterValue)
    .map((el:RespDTO) => ({...el,aDateField:el.aDateField.split('T')[0]}))),
    );
    }
    

Upvotes: 10

Views: 25730

Answers (4)

Can can use the follow code:

 execute = async (): Promise<BondAssetType[]> => {
       
    var response : Observable<BondAssetType[]> = this._assetBondTypeService.findAll().pipe(map(x => x.data));
    var result:BondAssetType[] = await firstValueFrom(response);

    return result;
}

Upvotes: -1

Jay McDoniel
Jay McDoniel

Reputation: 70131

If you are needing to do this from a Nest service, and return the result back to the client, you can simply return the observable and Nest will handle the subscription for you from there. If you need to do any extra data processing you can use the map operator after the .pipe() operator of an Observable. An example of this could be getting only the data from the axios response and not the entire response (which would have trouble with JSON.stringify() because it has circular references on itself).

The following is an example of such

import { Injectable, HttpService } from '@nesjts/common';
import { AxiosResponse } from 'axios';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class HttpConsumingService {
  private readonly DATA_URL = 'http://remote/data.json';
  constructor(private readonly http: HttpService) {}

  callHttp(): Observable<Array<Object>> {
    return this.http.get(this.DATA_URL).pipe(
      map((axiosResponse: AxiosResponse) => {
        return axiosResponse.data;
      })
    );
  }
}

From here, if you have a controller that calls this.httpConsumingService.callHttp(), Nest will call the service, subscribe to the observable, and return the data from it under the hood. No extra work needed. If you're looking for more info on Observables and the available operations learnrxjs.io is a pretty good source.

Upvotes: 14

vincenthavinh
vincenthavinh

Reputation: 462

You can also use HttpService (and so Axios) with Promises, if you are more familiar with promises and async/await thant with Observables :

const resp = await this.httpService.get(this.DATA_URL).toPromise(); // Here you get the AxiosResponse object.
const body = resp.data; // Here you get the response body, which is automatically parsed in the .data property of the AxiosResponse.

Or even :

const body = (await this.httpService.get(this.DATA_URL).toPromise()).data;

I am using await, but you could use classic promise syntax :

this.httpService.get(this.DATA_URL).toPromise()
.then(resp => {
    console.log(resp.data);
})
.catch(err => {
    // Handle Error Here
    console.error(err);
})

Upvotes: 0

richbai90
richbai90

Reputation: 5204

EDIT:

Disclaimer: I don't know much about Nest specifically, so this answer is from a purely vanilla JS perspective, different libraries have different built in abilities. What follows is an explanation of different ways to handle asynchronous requests and observables in javascript. I also highly recommend reading up on asynchronous javascript, observables, and promises as it will make your time in javascript far more pleasant.

Web requests in javascript happen asynchronously, meaning that they execute more or less in parallel with the rest of your synchronous code. You can imagine it like a separate thread, although it is not. This means that code that relies on the value from this web request must stall until the request is complete. From my original post below, the simplest option in your case is probably option 3. The code to use it might look a bit like this:

/**
 * A method in your rest controller that relies on the getSomething() 
 * method as implemented in option 2 below
 */
async showRemoteData() {  
  const remoteData = await appService.getSomething();
  // replace console.log with whatever method you use to return data to the client
  console.log(remoteData);
}

Original Answer

You cannot retrieve a value from an observable in a synchronous way. You have to subscribe to it and do something once the value has been returned, or convert it to a promise and return the promise. Your options are these:

// option 1 change getSomething to doSomething, and do everything in that method

doSomething(): Array<Object> {
  let resp = this.httpService.get(this.DATA_URL);
  resp.subscribe((value) => { // do something })
}

// option 2 return the observable and subscribe to it outside of that method
getSomething(): Array<Object> {
  return this.httpService.get(this.DATA_URL);
}
// outside of the AppService you can use it like this
appService.getSomething().subscribe((value) => {// do something})

// option 3 convert the observable to a promise and return it
getSomething(): Array<Object> {
  return this.httpService.get(this.DATA_URL).toPromise();
}
// outside of the AppService you can use it like this
let value = await appService.getSomething();
console.log(value);

Of the options, option 3 allows you to use async and await which is not synchronous but allows you treat the rest of your code in the async method as though it is, so that might be closest to what you want. I personally think option 2 is your best option though as you keep all the functionality of observables, including the whole sweet of operators available to you. Embrace asynchronous code in javascript, it is the best and often times only solution to many problems.

Upvotes: 5

Related Questions