Axios is a promise-based HTTP library that lets developers make requests to either their own server or a third-party server to fetch data. It offers different ways of making requests such as GET, POST, PUT/PATCH, and DELETE.

In this tutorial, you will learn how Axios interacts with applications, how Axios structures requests and responses, and how to use Axios to make requests to an API. You’ll also learn how to write tests for your Axios requests and how to automate them with CircleCI for fast, seamless feedback on your changes.

Prerequisites

To follow along with the tutorial, make sure that you have:

How does Axios work?

Axios helps developers make HTTP requests from NodeJS or XMLHttpRequests from a browser. If the request is successful, you will receive a response with the data requested. If the request fails, you will get an error. You can also intercept the requests and responses and transform or modify them. I will go into more detail about that later in this tutorial.

This diagram represents how Axios interacts with an application.

Axios interaction

Axios is able to determine whether the request is made from the browser or from NodeJS. Once this is established, it then identifies the proper way to make the API requests and returns a transformed response back to the client that made the request.

Axios request and response configurations

Making a basic request in Axios is easy because the only option required is the url. However, you can configure other options depending on the kind of request that you want to make.

Here is an example request:

const axios = require('axios');

const res = await axios.get(url, {
//We can add more configurations in this object
   params: {
  //This is one of the many options we can configure
   }
});

// This is the second configuration option
const res = await axios({
    method: 'get',
    url://Endpoint goes here,
    params:{

    }
});

The first example uses axios.get for a simple GET request where you can add additional configurations. The second example uses the full axios method, allowing you to specify more options such as the request method, URL, and parameters.

Axios provides great flexibility to configure your requests. You can decide to call Axios with the JavaScript dot notation format. Or you can use the object literal format to bundle all the Axios request properties into an object that can be used as properties of making the Axios request.

There are several methods that Axios supports for making requests. They include:

  • request
  • get
  • delete
  • head
  • options
  • post
  • put
  • patch

Here’s another code snippet that shows how to make a simple GET request sent to a Todos sample API using Axios.

axios({
  method: "get",
  url: "https://jsonplaceholder.typicode.com/todos",
  params: {
    _limit: 5,
  },
});

Axios responses

Once you send a request with Axios, you expect to have a response returned. This snippet shows the data structure of an Axios response:

{
  // `data` is the response that was provided by the server
  data: {},

  // `status` is the HTTP status code from the response
  status: 200,

  // `statusText` is the status message from the response
  statusText: 'OK',

  // `headers` are the HTTP headers that the server responded with
  headers: {},

  // `config` is the config that was provided to `axios` for the request
  config: {},

  // `request` is the request that generated the response
  request: {}
}

In this response, you get an object of the data that you are expecting, the status code sent back by the server, a statusText, response headers, the config object set by Axios, and the request object that was used to generate the response. You can then consume the responses on your client-side application depending on the data that you need.

Now that you’ve mastered some of the background, move on to the next section where I will show you exactly how to make HTTP requests with Axios.

Making Axios HTTP requests

In this section, you will make GET and PUT requests and observe concurrent requests. You will be using a free “fake” API: JSONPlaceholder.

You will also use an application that will help you make your requests and get a better view of what is happening under the hood.

Before you get started though, you need to clone the GitHub repository for the tutorial. Run these commands:

git clone https://github.com/CIRCLECI-GWP/making-http-requests-axios

cd making-http-requests-axios

Now you should have the files you need for this tutorial. Your next step is to install both Axios and Jest as dependencies. Run this command:

npm install

Open the index.html file on the browser to review your Axios demo webpage.

Axios demo webpage

There are three buttons for each request. Clicking a button should display a response after the Axios request has been made and data returned to the browser.

In the users.js file of your cloned repository, there is an event listener that displays the data returned by Axios. This file also has functions you will use to make your requests. Get started by making your first GET request.

Making a GET request

A basic GET request to our sample API looks like this:

axios
  .get("https://jsonplaceholder.typicode.com/users/1")
  .then((response) => {
    displayOutput(response);
  })
  .catch((err) => console.log(err));

This code snippet sends a GET request to the JSON API. Because the request returns a promise, we use the .then() block to handle the responses. We also need to use the .catch() method to log any errors to the console.

This code snippet is already in the getUser() function in the users.js file.

Now that you understand what is happening behind the scenes, go to the browser and click the GET button. Below it, new content should appear, displaying details of the response.

Axios GET request

This response from the API is broken into a few sections based on the data we have from Axios. The sections of the received Axios response are:

  • The status section, which displays the status code of the response. In this case it is 200, which means that the request was successful.
  • The headers section, which contains all the HTTP headers that the server responds with.
  • The data section, which contains the payload or the information that was requested from the server. In this case, it is all the information about user 1.
  • The config section, which contains all the configuration that was passed to Axios for the request.

As shown in the request, Axios behaves like the traditional fetch-API library. Considering that this is a GET request, you do not need to pass a body with a request. Next I will show you how to do that in a POST request using Axios.

Making a POST request

A POST request is a little different because you often need to pass some data in the request to the server. In the sample application, we have a POST request that creates a user and passes in details for that user. The code snippet for the request looks like this:

axios
  .post("https://jsonplaceholder.typicode.com/users", {
    id: 11,
    name: "Tom Brady",
    username: "Brad",
    email: "tombrad@asd.com",
  })
  .then((response) => displayOutput(response))
  .catch((err) => console.log(err));

The Axios POST request uses an object after the request URL to define the properties you want to create for your user. Once the operation has been completed, there will be a response from the server. To verify that your code is working, go back to the application browser window and click the POST button. This snippet is part of the postUser() function in the users.js file.

Axios POST request

A POST response differs a little from a GET request:

  • The status section has a status code of 201, which means a resource has been created. In this case, a new user has been created.
  • The headers section has a ‘content-length’ property for the length of the data we sent. It also specifies where the data will be stored: location.
  • The data section contains the information that was sent to the server.
  • The config section contains the configuration that was sent along with the request. This involves the method, url, and data being sent.

You can verify that the data defined in your POST request is the exact response received from the server as a created resource.

In this section, you learned how to make an Axios request, and you explored the basic structure of an Axios request and its the expected responses. In the next section, you will learn how to use request interception to verify data before it is sent to Axios as a request.

Axios request and response interceptors

In Axios, requests or responses can be intercepted before they are handled by the then() or catch() blocks. For example, say you want to check that all the requests going through to a client have a valid JWT token. To do that, you can set up a request interceptor to make sure that all the calls made to the server have that valid token. If a call does not have a valid token, the users of the system would have to go back to the login page and be reauthenticated.

In the interest of time, I will lead you through a less complicated use case, writing a logger for your current application.

The logger will log when a request was made, the URL for the request, and the time when the request was triggered. This code snippet is what the sample application uses:

axios.interceptors.request.use(
  (config) => {
    const today = new Date();
    console.log(
      `${config.method.toUpperCase()} request sent to ${
        config.url
      } at ${today.getHours()} : ${today.getMinutes()}`
    );
    return config;
  },
  (error) => {
    console.log(error);
  }
);

Axios will access the config section of your requests to display the request method, URL, and time. This code snippet is part of the users.js file in the interceptRequests() function of the sample project.

To observe the behavior of the Axios interceptor, open the Chrome browser console. Click the GET method that fetches the first user in the API.

![Axios POST request]2024-07-23-axios-request-interceptor

For the interceptors to work, they need to be called as concurrent requests to the jsonplaceholder API. The next code snippet shows Axios methods that implement the requests and responses:

const concurrentRequests = () => {
  interceptRequests();
  axios
    .all([
      axios.get("https://jsonplaceholder.typicode.com/users?_limit=5"),
      axios.get("https://jsonplaceholder.typicode.com/albums?_limit=5"),
    ])
    .then(
      axios.spread((users, albums) => {
        displayOutput(albums);
      })
    )
    .catch((err) => console.log(err));
};

The Chrome DevTools image shows that when you make any request, you can modify or inspect it before it is sent by Axios to the API server. Axios interceptors are not only powerful, but they also give developers the ability to control requests and the behavior of their responses.

Now you have learned how to use interceptors to modify or inspect your Axios requests and responses. Great work! Your next task is writing tests for your Axios implementation.

Testing the Axios implementation

Software testing is an integral process in the development of virtually every application. Tests help make sure your application works as intended and that quality is consistent throughout the development process. In this section of the tutorial, you will use the node.js version of Axios. Since you already installed Jest while installing dependencies, you can start writing tests right away.

In the root directory, there are two files named app.js and app.test.js. We use app.js for requests and app.test.js for tests.

In the app.js file, we use axios to make requests in Node.js. This method uses the same request structure as the browser method does:

const axios = require("axios");

const getUser = async () => {
  const getResponse = await axios
    .get("https://jsonplaceholder.typicode.com/users/1")
    .then((response) => response)
    .catch((err) => console.log(err));
  return getResponse;

  module.export = { getUser };
};

This snippet makes a request to the same URL used earlier and then saves the response to the getResponse variable. It then returns the response to make it accessible to other methods or functions that may need it — like our tests. The snippet exports the method to make it accessible outside the app.js file.

To write a test for this, we import the getUser method into the app.test.js test file. Then we write the test as shown here:


const { getUser, postUser, concurrentRequests } = require("./app.js");

describe("Axios requests suite", () => {
  test("should get a single user", async () => {
    const response = await getUser();
    expect(response).not.toBeNull();
    expect(response.status).toBe(200);
    expect(response.data.address.street).toContain("Kulas");
  });
}

Using this test and the sample response object, you can verify that the data called from the Axios endpoint was in fact returned and that your API call was successful.

In the same file, we have all three tests:

test("should post a new user", async () => {
  const response = await postUser();
  expect(response).not.toBeNull();
  expect(response.status).toBe(201);
  expect(response.data.username).toBe("Brad");
});

test("should make simultaneous axios requests", async () => {
  const response = await concurrentRequests();
  expect(response).not.toBeNull();
  expect(response.status).toBe(200);
});

Now that you understand how the tests work, run them:

npm run test

Again, success!

npm run tests

Now that you have written Axios requests and tested them, you can build a continuous integration(CI) pipeline for them. Using a CI/CD tool like CircleCI keeps everyone informed when changes break the pipeline and cause the tests to fail. This kind of feedback loop provides the insight and transparency needed in a successful software development process.

Integrating with CircleCI

The sample project contains a .circleci folder and inside it, a file named config.yml. This is CircleCI’s configuration file. In the file, we have the configuration for our project:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: cimg/node:20.3.1
    steps:
      - checkout
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: install dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Axios request tests
          command: npm run test
      - store_artifacts:
          path: ~/repo/axios-http-requests

This configuration defines your working_directory, then tells CircleCI to use a Node image to execute your tests. Once that is set up, it checks for any stored cache for the project. If there is one, it needs to be restored before doing a new dependencies install. The cache is saved after installing new dependencies, tests are executed, and any artifacts that were generated are saved.

To run the project in CircleCI, it needs to exist as a repository on your GItHub. First, create an empty repository on GitHub. Next, change the origin URL to your repo:

git remote set-url origin <your-repo-url>

Then, push the repository:

git push

Once this is done, log into the CircleCI dashboard. In the Organization homepage, click Create Project. Select GitHub, then pick your project and give it a meaningful name. Finally, click Create Project.

Setting up the project

CircleCI will automatically detect your configuration file, but it won’t run the pipeline. To trigger a pipeline build, push a small, meaningless commit.

You have a successful pipeline build! Review details by clicking Jobs then build.

Opening build workflow

Click the steps you want more detail about; for example the Axios requests tests step.

Axios request steps

CircleCI detects changes every time you push to the GitHub repository in your main branch. The CircleCI pipeline will execute again and make sure that your test suite runs, ensuring a successful continuous integration process.

I enjoyed creating this tutorial for you, and I hope you found it valuable. Until next time, keep coding!


Waweru Mwaura is a software engineer and a life-long learner who specializes in quality engineering. He is an author at Packt and enjoys reading about engineering, finance, and technology. You can read more about him on his web profile.

Read more posts by Waweru Mwaura