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:
- NodeJS installed in your system
- A GitHub account
- A CircleCI account
- A basic understanding of JavaScript and unit testing
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 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.
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.
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 is200
, 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 aboutuser 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.
A POST
response differs a little from a GET
request:
- The
status
section has a status code of201
, 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 themethod
,url
, anddata
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]
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!
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.
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.
Click the steps you want more detail about; for example the Axios requests tests
step.
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!