How to use Axios in NestJs

Learn how to use NestJs with Axios and the HTTP Module. A richly featured HTTP client package

Kobbi Gal

Posted August 21, 2022

18 min read

Learn NestJs the right way

Skyrocket your NestJs skills to the top

with Vladimir Agaev
Full Stack Developer
  • checkNestJs Architecture
  • checkSessions
  • checkCaching
  • checkPagination
  • checkScaling with Redis
  • checkUnit Testing
  • checkRBAC & CronJobs

In this article, we're going to explore how we use axios in NestJS with a practical example. You can find the source code here.

This article relies on the official documentation about the HTTP Module

Introduction

Sending and receiving data from other services using an interface is fundamental in most modern web applications.

JavaScript, both on the client and server side, supplies developers with many options to achieve this.

On the front-end, we have:

  • The very popular FETCH API
  • The now obsolete XMLHttpRequest
  • jQuery.ajax() function

On the back-end, in NodeJs, we have:

All of them allow developers to send and receive HTTP requests.

You've noticed that I have not mentioned axios. Why? Because axios can run both in front-end and in the back-end!

It's a fan favourite among full-stack developers who do not need to learn separate libraries and syntax when switching between back-end and frontend. For that, Axios is called isomorphic!

Under the hood, axios uses the node http module or the XMLHttpRequest depending on whether the code runs on the back end or front end.

Since axios is isomorphic, easy-to-use, but powerful, it is no surprise that the NestJS framework decided to bake axios into one of its modules, the HTTP Module.

Prerequisites

To be able to follow the article, you'll need to have:

A Word about Modularity

The NestJS modular architecture enforces the Separation of Concerns (SoC) design principle by isolating an application's functions into independent pieces or building blocks, each containing all the parts needed to execute a single aspect of the functionality.

There are several advantages to this type of architecture:

  • Code legibility and easier debugging. It is much easier to read and debug code when split into separate modules, as they tend to be smaller logic units and interactivity between the module parts

  • Improves manageability. It is much easier for a development team lead to split the modules into different development squads, each focusing on their particular area. It is also easier to design, test and implement the modules into the application.

  • Code reuse. You can import and inject modules into other modules, requiring less development effort and redundant code. For example, you can have reusable modules or utilities.

  • Collaboration. Developers from different teams can collaborate on the same application without blocking each other from working on their respective modules.

soc

Now that we understand the NestJS philosophy and architecture let's examine how we can use axios with two examples.

How to Use axios in NestJS

There are two main ways to use axios in NestJS:

NestJs HTTP Module

Before we start, let's import the needed axios class declarations. For that we must install NestJs HTTP module dependency using npm:

npm i --save @nestjs/axios

Now that we have axios saved in node_modules/@nestjs/axios, we can take a quick glance at how NestJS implemented axios into its own module. If you're not interested in understanding the nitty-gritty of NestJS implementation of axios, you can skip this section to Adding the API module to see an example of how to use axios within NestJS within a simple API.

NestJS Implementation of axios

We know from reading the NestJS documentation on the HTTP Module that:

Nest wraps axios and exposes it via the built-in HTTP Module. The HTTP Module exports the HttpService class, which exposes axios-based methods to perform HTTP requests. The library also transforms the resulting HTTP responses into Observables.

Let's take a look under-the-hood and see exactly how this is done.

Reviewing the code behind HTTP Module (can be found in node_modules/@nestjs/axios/dist/http.module.d.ts), we can see that two interfaces are imported:

import {
  HttpModuleAsyncOptions,
  HttpModuleOptions,
} from "./interfaces";

If we follow the code behind these two interfaces, we find that HttpModuleOptions imports AxiosRequestConfig and uses it as its type:

import { AxiosRequestConfig } from "axios";

export declare type HttpModuleOptions = AxiosRequestConfig;

AxiosRequestConfig exposes options for the AxiosInstance to use (such as request parameters, headers, etc.). The same goes for HttpModuleAsyncOptions but for registration of the HTTP Module for asynchronous requests.

Since the HTTP Module class exports the HttpService class, we should also open and inspect node_modules/@nestjs/axios/dist/http.service.d.ts, where can see that the first line in HttpService class declaration is an import of axios-related interfaces:

import { AxiosInstance, AxiosPromise, AxiosRequestConfig, AxiosResponse };

It then basically creates an AxiosInstance when HttpService class is instantiated and inherits the HTTP methods from axios so that HttpService can execute the HTTP call using HttpService.get instead of directly using axios.get:

export declare class HttpService {
  protected readonly instance: AxiosInstance;
  constructor(instance?: AxiosInstance);
  get<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Observable<AxiosResponse<T>>;
  //...
  post<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Observable<AxiosResponse<T>>;
  //...
}

Adding the API Module

Let's create a simple NestJS app using the CLI:

nest new nestjs-axios-tutorial

Next, remove some of the generated files as we'll be creating our own module from scratch:

rm app.{service,controller*}.ts

Now that we have a clean application with the very basics, let's generate a new module to hold our API:

nest generate module api

CREATE src/api/api.module.ts (80 bytes)
UPDATE src/app.module.ts (304 bytes)

Great, this generated the api folder and module. At this point, the module is empty:

api.module.ts
import { Module } from "@nestjs/common";

@Module({})
export class ApiModule {}

Since we want to implement a simple API, we'll need the API module to have a request handler and service to hold the business logic. More importantly, as explained in the NestJS documentation, we'll need to also import the HTTP Module into our API module.

First, let's create two new files to hold our service and controller:

nest generate controller api

CREATE src/api/api.controller.spec.ts (471 bytes)
CREATE src/api/api.controller.ts (95 bytes)
UPDATE src/api/api.module.ts (162 bytes)

nest generate service api

CREATE src/api/api.service.spec.ts (439 bytes)
CREATE src/api/api.service.ts (87 bytes)
UPDATE src/api/api.module.ts (233 bytes)

We can see that our API module was updated twice to set the providers and the controllers to the generated ones:

api.module.ts
import { Module } from "@nestjs/common";
import { ApiController } from "./api.controller";
import { ApiService } from "./api.service";

@Module({
  controllers: [ApiController], // ApiController added
  providers: [ApiService], // ApiService added
})
export class ApiModule {}

Before we leave the API module, we need to import the HTTP Module:

api.module.ts
import { HttpModule } from "@nestjs/axios";
import { Module } from "@nestjs/common";
import { ApiController } from "./api.controller";
import { ApiService } from "./api.service";

@Module({
  imports: [HttpModule], // imported axios/HttpModule
  controllers: [ApiController],
  providers: [ApiService],
})
export class ApiModule {}

That's it for the API module, it's all wired up! In the next section, we'll inject the HttpService into our API service and use it to reach out to a publicly-available API to get some dummy data and set up the controller to expose a path to retrieve this data from the service.

Set up our Service and Controller

Let's review what our service looks like at the moment:

api.service.ts
import { Injectable } from "@nestjs/common";

@Injectable()
export class ApiService {}

Looks pretty empty. Let's list the things that we need to in this module to be able to retrieve some data using axios:

  1. Inject HttpService into ApiService so we can instantiate and use its singleton instance to retrieve data from within the service functions.
  2. Create a service function that uses HttpService to retrieve the data.
  3. Create a controller method to call the service function and return the data to the client.

To do the first step, we need to import and add the Injectable decorator to the ApiService class. Then, we need to import and add the HttpService into the constructor. The API service will look like this after making the necessary changes:

api.service.ts
import { Injectable } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";

@Injectable()
export class ApiService {
  constructor(private http: HttpService) {}
}

We marked the injected HttpService as private because we don't want to expose it and its properties outside of this class.

Next, let's create a simple function inside the service to execute an HTTP request to retrieve some data. This HTTP request will be done using the injected HttpService instance. The NestJS documentation tells us that:

All HttpService methods return an AxiosResponse wrapped in an Observable object.

If you've worked with rxJS, you should be familiar with Observables. Observables are used for transferring messages between parts of an application using event handling. Unlike Promises, which deal with one asynchronous event at a time, Observables handle a sequence of asynchronous events over a period of time.

If you have no experience working with Observables, don't worry, we will cover how to use Promises as well in a separate service function.

Using HTTP Module with Observable

For the first service function, which we will implement using an Observable, we'll use CoinDesk's API. It allows us to retrieve the current price of BitCoin in different currencies without authentication.

Let's run a request and see what the response looks like:

curl https://api.coindesk.com/v1/bpi/currentprice.json --silent | jq
{
  "time": {
    "updated": "Aug 18, 2022 15:47:00 UTC",
    "updatedISO": "2022-08-18T15:47:00+00:00",
    "updateduk": "Aug 18, 2022 at 16:47 BST"
  },
  "disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org",
  "chartName": "Bitcoin",
  "bpi": {
    "USD": {
      "code": "USD",
      "symbol": "&#36;",
      "rate": "23,463.0041",
      "description": "United States Dollar",
      "rate_float": 23463.0041
    },
    "GBP": {
      "code": "GBP",
      "symbol": "&pound;",
      "rate": "19,605.4985",
      "description": "British Pound Sterling",
      "rate_float": 19605.4985
    },
    "EUR": {
      "code": "EUR",
      "symbol": "&euro;",
      "rate": "22,856.3915",
      "description": "Euro",
      "rate_float": 22856.3915
    }
  }
}

Let's say we're only interested in retrieving the price of Bitcoin in US Dollars:

curl https://api.coindesk.com/v1/bpi/currentprice.json --silent | jq -r '.bpi.USD.rate'

23,486.4082

Let's see how we would get this same data from our service:

api.service.ts
import { HttpService } from '@nestjs/axios';
import { ForbiddenException, Injectable } from '@nestjs/common';
import { map, catchError } from 'rxjs';

@Injectable()
export class ApiService {
  constructor(private http: HttpService) {}

  async getBitcoinPriceUSD() {
    return this.http
      .get('https://api.coindesk.com/v1/bpi/currentprice.json')
      .pipe(
        map((res) => res.data?.bpi),
        map((bpi) => bpi?.USD),
        map((usd) => {
          return usd?.rate;
        }),
      )
      .pipe(
        catchError(() => {
          throw new ForbiddenException('API not available');
        }),
      );
  }
}

Let's review what was added above:

  • We created the getBitcoinPriceUSD function which returns an Observable<AxiosResponse<T>>.
  • We send a GET request using HttpService to our endpoint of interest.
  • We then pipe the AxiosResponse to 3 mappers that extract the Bitcoin price from the nested response object.
  • We catch any errors we have traversing the nested object.

From here, all we need to do is add a controller that calls getBitcoinPriceUSD.

api.controller.ts
import { Controller, Get } from "@nestjs/common";
import { ApiService } from "./api.service";

@Controller("api")
export class ApiController {
  constructor(private apiService: ApiService) {}

  @Get("price/bitcoin")
  getBitcoinPrice() {
    return this.apiService.getBitcoinPriceUSD();
  }
}

NestJS will call the service, subscribe to the Observable, and return the data from it under the hood. No extra work needed!

Let's run the server and send a request to see what we get:

yarn start:dev

yarn run v1.22.18
$ nest start --watch
[8:01:08 pm] Starting compilation in watch mode...

[8:01:09 pm] Found 0 errors. Watching for file changes.

[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [NestFactory] Starting Nest application...
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [InstanceLoader] AppModule dependencies initialized +22ms
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [InstanceLoader] HttpModule dependencies initialized +0ms
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [InstanceLoader] ApiModule dependencies initialized +1ms
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [RoutesResolver] ApiController {/api}: +4ms
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [RouterExplorer] Mapped {/api/price/bitcoin, GET} route +2ms
[Nest] 40313  - 18/08/2022, 8:01:10 pm     LOG [NestApplication] Nest application successfully started +3ms
curl http://localhost:3000/api/price/bitcoin

23,313.5305

Using HTTP Module with Promise

As mentioned earlier, if you've not worked with Observables before, we've got you covered. In this section, we'll write a service function that uses a Promise to retrieve a requested resource from an open API.

In this second service function, we will pull some random facts about cats using the Cat Fact API to retrieve random facts about everyone's favorite furry pet.

As we know from the previous section's implementation of HttpService, the type returned is an Observable<AxiosResponse<T>>. So, how do we transform an Observable into a Promise?

Lucky for us, the developers of rxJS already thought about this and created a dedicated function named lastValueFrom. In their documentation, the describe this function as:

Converts an observable to a promise by subscribing to the observable, waiting for it to complete, and resolving the returned promise with the last value from the observed stream.

This is a very neat trick or workaround! Let's see how we implement it to retrieve a random cat fact.

Let's send a simple request to the Cat Fact API to understand the structure of the response:

curl https://catfact.ninja/fact --silent | jq
{
  "fact": "In 1987 cats overtook dogs as the number one pet in America.",
  "length": 60
}

As we can see, the interesting fact can be found under the /fact path.

Building on this information, we know that we need, just like in the first service function, to use map to access the nested object of interest.

In addition, if we review the example given for lastValueFrom in the link above, we know that we need to use await to actually retrieve the response from the Promise.

This is how the function will look like:

api.service.ts
async getCatFacts() {
   const request = this.http
     .get('https://catfact.ninja/fact')
     .pipe(map((res) => res.data?.fact))
     .pipe(
       catchError(() => {
         throw new ForbiddenException('API not available');
       }),
     );

   const fact = await lastValueFrom(request);

   return {
     data: {
       fact,
     },
   };
 }

So we send a GET request, pipe the response into a mapper and extract the fact. As in the first example, we also add some error handling just in case the service is unavailable.

The more interesting line is when we define the fact variable to hold the value from the returned Promise in the form of lastValueFrom. This is the whole point of this section.

Let's create a handler in the controller to get call the getCatFacts method:

api.controller.ts
@Get('facts/cats')
 getCatFacts() {
   return this.apiService.getCatFacts();
}

And test it out:

curl http://localhost:3000/api/facts/cats --silent | jq
{
  "data": {
    "fact": "Cats can be right-pawed or left-pawed."
  }
}

Given the simple examples above, you should now feel ready to use both methods (Observable/Promise) with NestJS's HTTP Module!

Axios as a standalone library

Before jumping into a brief example of how to use axios as a module within NestJS, a disclaimer is advised since this method is not recommended as it goes against the modular philosophy that NestJS adheres by and explained in the above section. The recommended way to use axios is mentioned in the Set up our Service and Controller section using HttpService/HttpModule. But, we also believe in the freedom to choose whichever method you see fit for your use case and maintainability.

Let's rewrite our cat fact API to use axios instead of HttpService. For those of us who are familiar with axios, we know that we need to supply it with two arguments:

  • HTTP method in the method argument.
  • URL of the endpoint.

In the case of retrieving a cat fact from the aforementioned API, we know that we want to send a GET request so we'll use axios.get. We also know that axios.get will return a Promise that is either fulfilled or rejected. So, in order to retrieve the actual response containing the random cat fact, we would either use a callback or await. In this example, we'll use await to get the response and then return that response to the controller.

The full code of this service method can be seen below:

api.service.ts
import axios from 'axios';

// ...

async getCatFactsWithAxiosLib() {
    const response = await axios({
      method: 'GET',
      url: 'https://catfact.ninja/fact',
    }).catch(() => {
      throw new ForbiddenException('API not available');
    });

    return {
      data: {
        fact: response.data?.fact,
      },
    };
  }

We then add a handler for this method in our controller:

api.controller.ts
@Get('facts/cats/deprecated')
  getCatFactsWithAxiosLib() {
    return this.apiService.getCatFactsWithAxiosLib();
  }

And send a request:

curl http://localhost:3000/api/facts/cats/deprecated --silent | jq
{
  "data": {
    "fact": "A cat only has the ability to move their jaw up and down, not side to side like a human can."
  }
}

Summary

I hope you have enjoyed this tutorial!

In this article we have learned how to use Axios with NestJs using the HTTP Module where we used Observable and Promise. We also have seen how to directly import axios into NestJs

Learn NestJs the right way

Skyrocket your NestJs skills to the top

with Vladimir Agaev
Full Stack Developer
  • checkNestJs Architecture
  • checkSessions
  • checkCaching
  • checkPagination
  • checkScaling with Redis
  • checkUnit Testing
  • checkRBAC & CronJobs