Axios is a promise-based HTTP library that lets developers make requests to either their own 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, I will explain how Axios interacts with applications, describe the structure of Axios requests and responses, how to make requests to an API, and how to write tests for your requests using CircleCI.

Prerequisites

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

How does Axios work?

Axios works by making HTTP requests with NodeJS and XMLHttpRequests on the browser. If the request was successful, you will receive a response with the data requested. If the request failed, 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 shows a representation of how Axios interacts with an application.

Axios interaction

Axios is able to determine whether the request is made to the browser or to 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 server 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:{

    }
});

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 and that are allowed to make requests. They include:

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

The next code snippet shows how to use a sample 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.

Is this feeling like a lot of theoretical knowledge? If so, 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 axios-http-requests

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

For now, the buttons do not do anything. We will build the functionality later in the tutorial.

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

This snippet shows what your request should look like:

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, you will use the .then() block to handle the responses. You will also need to use the .catch() method to log any errors to the console.

Add the previous code snippet to the getUser() function in the users.js file and save it.

Next, go to the browser and click the GET button. Below it, new content should appear, displaying details of the response.

Axios GET request

Styling and display has already been completed. 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 will be passing some data in the request to the server. In the request, you will be creating a user and passing in details for that user. The code snippet for the request will look something 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 I have described the basic structure of an Axios request and its the expected responses. In the next section, I will explain how to request interception to verify data before it is sent to Axios as a request.

Axios request and response interceptors

Interception in Axios happens when requests are intercepted before they are handled by the then() or the catch() code block. For example, say you want to check that all the requests going through to a client have a valid JWT token. You would 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 log in page and be re-authenticated. In the interests 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. Use this code snippet:

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

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, they 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

Writing tests is an integral process in 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, create two files and name them app.js and app.test.js. Use app.js for your requests and app.test.js for your tests. You need to re-create the app.js file to make sure that you can access the methods that use the Node.js Axios — not the browser Axios defined in the script section of your index.html file.

In the app.js file, you can 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, import the getUser method into your app.test.js test file. Then write your 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 indeed the data called from the Axios endpoint was returned and that your API call was successful. To run this test, go to your terminal and use the Jest command npm test:

 PASS  ./app.test.js (11.788 s)
  Axios requests suite
    √ should get a single user (1179 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        13.807 s
Ran all test suites.

Success! Now you can write more tests, for the POST request and the concurrent requests using Axios.

In the same file, add these 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);
});

When you are done, rerun your tests.

npm run test

Again, success!

npm run tests

Now that you have written Axios requests and tested them, you can build a 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

Create a .circleci folder and inside it, a file named config.yml. This is CircleCI’s configuration file. In the file, add this configuration:

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.

Now you need to save the configuration file, commit, and push the changes to your GitHub repository. Once this is done, log into the CirclecI dashboard. Find the GitHub repository for the tutorial in the Projects section. In our case, it is called axios-http-requests. Click Set Up Project.

Setting up the project

When prompted, enter the name of the branch that has your config.yml file. For this tutorial, it is the main branch. Then, click Set up Project.

Select configuration

You have a successful pipeline build! Review details by clicking the build workflow.

Opening build workflow

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

Opening build workflow

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 continous 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