I'm new to Nestjs, Typescript and basically backend development. And I'm working on a simple Weather app, where I fetch weather data from Open Weather API.
I'm using Nest built-in HttpModule
which is wrapping Axios within, then using HttpService
to make a GET request to Open weather. The request is returning an Observable which is totally news to me.
How do I extract the actual response data from the observable in the Injectable service
and return it to the Controller
?
Here's my weather.service.ts
import { Injectable, HttpService } from '@nestjs/common';
@Injectable()
export class AppService {
constructor(private httpService: HttpService) {}
getWeather() {
let obs = this.httpService.get('https://api.openweathermap.org/data/2.5/weather?q=cairo&appid=c9661625b3eb09eed099288fbfad560a');
console.log('just before subscribe');
obs.subscribe((x) => {
let {weather} = x.data;
console.log(weather);
})
console.log('After subscribe');
// TODO: Should extract and return response data f
// return;
}
}
And this is weather.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getWeather() {
const res = this.appService.getWeather();
return res;
}
}
Also can someone clarify what are the missing types in my code?
RxJS Observables
are essentially advanced callbacks. Because they work in an asynchronous fashion, you need to have your code deal with that. Nest can handle if an Observable is returned from the controller and will subscribe to it under the hood for you, so all you should need to do in your service is something like this:
import { Injectable, HttpService } from '@nestjs/common';
@Injectable()
export class AppService {
constructor(private httpService: HttpService) {}
getWeather() {
return this.httpService.get('https://api.openweathermap.org/data/2.5/weather?q=cairo&appid=c9661625b3eb09eed099288fbfad560a').pipe(
map(response => response.data)
);
}
}
The map
is imported from rxjs/operators
and is similar to Array.prototype.map
in that it can take values in and transform them as necessary. From here, your Controller
just needs to return this.appService.getWeather()
, and Nest will handle the rest.
The other option you have is to convert the observable to a promise using .toPromise()
and then you can use your usual async/await
syntax, which is another valid choice.
RxJS v7 and on
toPromise()
was deprecated in RxJS v7. It's now recommended to use lastValueFrom(observable)
or firstValueFrom(observable)
instead to make an observable async.