Reputation: 358
I recently started learning NestJS as it seems like a wonderful framework to help build the backend of my projects. What I need to learn, and am having trouble finding, is a way to get data from a 3rd-party API. According to the docs, it wraps Axios, which is great since I know Axios pretty well. What I cannot understand is how to get it to work.
I am trying, for learning purposes, to get weather data from OpenWeatherMap.org. When I scaffolded the app, I used nest new weather-app
and generated, in order, a weather module, service and controller to make sure everything was properly integrated with nest g <<<type of file>>> weather
. You can safely assume that my app.module.ts
correctly has my weather.module.ts
imported. Also, my OpenWeatherMap API key is copied straight over into my .env
.
Here is my weather.module.ts
:
require('dotenv').config();
import { Module, HttpModule } from '@nestjs/common';
import { WeatherService } from './weather.service';
import { WeatherController } from './weather.controller';
@Module({
imports: [
HttpModule.register({
baseURL: 'api.openweathermap.org/data/2.5/weather',
params: {
appid: process.env.OPEN_WEATHER_KEY,
},
}),
],
providers: [WeatherService],
controllers: [WeatherController],
})
export class WeatherModule {}
This is my weather.service.ts
:
import { Injectable, HttpService } from '@nestjs/common';
import { AxiosResponse } from 'axios';
import { Observable } from 'rxjs';
@Injectable()
export class WeatherService {
constructor(private readonly httpService: HttpService) {}
forCity(city: string): Observable<AxiosResponse<object>> {
return this.httpService.get(`?q=${city}`);
}
}
And this is my weather.controller.ts
:
import { Controller, Get, Param } from '@nestjs/common';
import { WeatherService } from './weather.service';
@Controller('weather')
export class WeatherController {
constructor(private readonly weatherService: WeatherService) {}
@Get(':city')
getWeather(@Param('city') city: string): object {
return this.weatherService.forCity(city);
}
}
When I run this project, Nest successfully builds everything without error. When I go to Postman, however, and try to hit my endpoint, I get this:
I think I've followed the NestJS docs fairly closely, as well as using my own experience with Axios, but I can't figure out what I'm not doing correctly here. I have a larger project that I would like to use Nest in, but I would need to hit a 3rd-party API for that project to work.
Any ideas?
Thanks in advance!
Update:
After using the Observable's pipe
method and catchError
, I got a 401
status. At first, I thought this meant that my api key wasn't being passed through correctly, but I logged this out and it matches what is in my account for the weather api.
2nd Update: A couple of days later (including a night off) and I'm still not having any success. If I move my url and API key in to weather.services.ts
, I get a circular reference. I've read the documents for Nest and Axios through and through it feels like and I still can't get this to work.
I also tried to access the axiosRef()
that Nest exposes through HttpService
, but ended up getting a Typescript compile error to convert my Observable
type to a Promise
. All that did was give me another circular reference.
Upvotes: 10
Views: 18434
Reputation: 15204
HttpModule and HttpService (from the "@nestjs/common" package) are deprecated and will be removed in the next major release. Please, use the "@nestjs/axios" package instead.
app.module.ts
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [
HttpModule,
],
})
export class AppModule {}
app.controller.ts
@Get()
getWeatherForecasts() {
return this.appService.getWeatherForecasts();
}
app.service.ts
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
async getWeatherForecasts() {
const url = 'http://www.7timer.info/bin/api.pl?lon=113.17&lat=23.09&product=astro&output=json';
const { data } = await firstValueFrom(this.httpService.get(url));
return data;
}
Upvotes: 13
Reputation: 91
In your service, when you return the output, NestJS tries to "stringify" the output which is of type Observable<AxiosResponse>. This would result in an error as the return type above is a circular structure. I would suggest piping the response.
Try this for the weather.service.ts
:
import { Injectable, HttpService } from '@nestjs/common';
import { AxiosResponse } from 'axios';
import { Observable } from 'rxjs';
@Injectable()
export class WeatherService {
constructor(private readonly httpService: HttpService) {}
forCity(city: string): Observable<AxiosResponse<object>> {
return this.httpService
.get(`?q=${city}`)
.pipe(
map((axiosResponse: AxiosResponse) => {
return axiosResponse.data;
}),
);
}
}
Upvotes: 3