jstrother
jstrother

Reputation: 358

How to use NestJS to get data from 3rd-party API

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:

nest-weather app postman error

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

Answers (2)

Dmitry Grinko
Dmitry Grinko

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

sagarsinha7777
sagarsinha7777

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

Related Questions