When we build applications that require us to make a network request, either to our backend or a third-party API, we use HTTP clients like Axios and Fetch to perform such requests.
In this guide, we introduce Axios and Fetch and compare them so that we can make an informed decision on which one to select.
A Quick Overview of Fetch and Axios The Fetch API is an interface that exposes a method called fetch() for making a network request. It is built into modern browsers and so no installation is required. It is also available as an experimental feature in node.js .
Axios is a third-party library that we can add to our project either via a Content Distribution Network or CDN, or install it via a package manager, like npm or yarn. Axios can run within a browser or a node.js environment.
Fetch and axios are both promise-based HTTP clients. This means that when we use them to make a network request, they return a promise that can either resolve or reject.
Installing Axios If we’re using Axios on a node.js environment, we can use one of the following installation methods:
Install using NPM: Install using Yarn: We must then import into our project:
import axios from "axios";
If we’re using Axios within a browser, we can use a CDN:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
You can read all the ways to install Axios here . Now that everything is installed, let’s compare them.
Comparing the Features of Fetch and Axios Let’s start with the syntax.
Syntax Fetch accepts two arguments. The first argument is the URL for the resource we want to fetch. The second is an optional argument which is an object containing the configuration options for making requests. Thus, the syntax is:
Without the configuration option, the request will default to making a GET request:
With the configuration option, we can define some custom settings for the request which include:
fetch(url, {
method: 'GET', // other options: POST, PUT, DELETE, etc.
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({}),
})
Axios syntax is similar, but there are many different ways of calling it:
axios(url, {
// configuration options
})
We can also append the HTTP method like this:
axios.get(url, {
// configuration options
})
Like the fetch method, we can ignore HTTP methods in axios to default to GET like so:
Likewise, we can define some custom settings for the request using the second argument:
axios(url, {
method: 'get', // other options: post, put, delete, etc.
headers: {},
data: {},
})
We can also write it like this:
axios({
method: 'get',
url: url,
headers: {},
data: {},
})
Now let's examine the different way Axios and fetch handle responses.
Handling JSON Data In the example below, we perform a GET request to a REST API called JSONPlaceholder to fetch a list of todos items using both fetch and Axios and compare the differences.
Using the Fetch API, our code looks like this:
const url = "https://jsonplaceholder.typicode.com/todos";
fetch(url)
.then(response => response.json())
.then(console.log);
The result in our console looks like this:
The fetch() returns a promise whose response is handled by the .then() method. At this point, we do not have the JSON data format that we need, so we call the .json() method on the response object. This returns another promise that is resolved with the data in JSON form.So a typical fetch request contains two .then() calls.
However, if we use Axios to perform the same fetch, we have the following code:
const url = "https://jsonplaceholder.typicode.com/todos";
axios.get(url)
.then(response => console.log(response.data));
With Axios, the response data is available as JSON by default. The response data is always available on the data property of the response object.
We can override the default JSON data type by specifying the responseType in the configuration option like so:
axios.get(url, {
responseType: 'json' // options: 'arraybuffer', 'document', 'blob', 'text', 'stream'
})
Auto Stringify Now let’s try sending data using the JSONPlaceholder API.
We will need to serialize our data into a JSON string to do this. When we send JavaScript objects to the API using a POST method, Axios automatically stringifies the data.
The following code performs a post request using Axios:
const url = "https://jsonplaceholder.typicode.com/todos";
const todo = {
title: "A new todo",
completed: false
}
axios.post(url, {
headers: {
'Content-Type': 'application/json',
},
data: todo
})
.then(console.log);
When we make a post request with axios, we assign the data to be sent as the request body to the data property. We can also set the content type header. By default, axios sets Content-Type to application/json.
Let's take a look at the response object:
The response data is located on the response.data like so:
.then(response => console.log(response.data));
If we use the Fetch API, we have to manually stringify the object using JSON.stringify() and then assign it to the body of our request.
const url = "https://jsonplaceholder.typicode.com/todos";
const todo = {
title: "A new todo",
completed: false
};
fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(todo)
})
.then((response) => response.json())
.then((data) => console.log(data))
We must also explicitly set the Content-Type to application/json with Fetch.
Error Handling Both fetch and axios return a promise that is either resolved or rejected. When the promise rejects we can use .catch() to handle the error. How we handle errors with axios is more succinct when compared to the Fetch method.
Starting with the axios, a typical error handling with .catch() looks like so:
const url = "https://jsonplaceholder.typicode.com/todos";
axios.get(url)
.then((response) => console.log(response.data))
.catch((err) => {
console.log(err.message);
});
Axios’s promise will reject if the status code falls outside of the range of 2xx. We can determine more information about the error by checking whether the err object contains a response or a request property like so:
.catch((err) => {
// handling error
if (err.response) {
// Request made and server responded
const { status, config } = err.response;
if (status === 404) {
console.log(`${config.url} not found`);
}
if (status === 500) {
console.log("Server error");
}
} else if (err.request) {
// Request made but no response from server
console.log("Error", err.message);
} else {
// some other errors
console.log("Error", err.message);
}
});
A response property on the error object indicates that the client received an error response with a status code outside of the 2xx range. A request property on the error object indicates that a request was made, but the client did not receive a response. Otherwise, if there is no request or response property, an error occurred in setting up the network request.
Fetch will not reject a promise if we get a 404 error or any other HTTP error. Fetch only rejects a promise when the network fails. So we have to manually handle HTTP errors within .then clauses.
Let's take a look at the following code:
const url = "https://jsonplaceholder.typicode.com/todos";
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(
`This is an HTTP error: The status is ${response.status}`
);
}
return response.json();
})
.then(console.log)
.catch(err => {
console.log(err.message);
});
In the response block, we are checking if the response’s ok status is false, then we throw a custom error that is handled in the .catch block.
We can take a look at the methods available on the response’s object like so:
The above screenshot is for a successful fetch. In a situation where we hit a wrong URL endpoint, the ok and status property will become false and 404 respectively, we throw an error and so the .catch() clause will display our custom error message.
Response Timeout/ Cancel Requests Let’s see how these HTTP clients handle response timeout for HTTP requests. With Axios, wecan add a timeout property in the configuration object and specify the time in milliseconds before the request terminates.
In the following snippet, we aim to terminate the request if it takes longer than four seconds and then log an error in the console.
const url = "https://jsonplaceholder.typicode.com/todos";
axios.get(url, {
timeout: 4000, // default is `0` (no timeout)
})
.then((response) => console.log(response.data))
.catch((err) => {
console.log(err.message);
});
To cancel requests with Fetch, we can use the AbortController interface . See the usage below:
const url = "https://jsonplaceholder.typicode.com/todos";
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => controller.abort(), 4000);
fetch(url, {
signal: signal
})
.then((response) => response.json())
.then(console.log)
.catch((err) => {
console.error(err.message);
});
We started by creating a controller object and gained access to the signal object and the abort() method. We then passed the signal object to fetch() via the configuration option. With this, the fetch request will terminate whenever the abort method is called. As we can see, with the help of setTimeout function, the operation terminates if the server does not respond in four seconds.
Performance Since Fetch and axios are both promise-based, they should not cause any performance issues. However, we can still measure their performance using measurethat.net .
After successive testing using the online tool, we have the following result :
The graph:
As seen above, the native Fetch is slightly faster than axios. This is insignificant as both clients are asynchronous.
Browser Support Axios and Fetch are widely supported among modern browsers. For older environments like IE 11 that doesn’t support ES6 Promises , we have to use a polyfill . Also for Fetch, specifically, we will add another polyfill to support implementations in older browsers .
Conclusion In this guide, we discussed Fetch and axios and practically compare them using real-world scenarios. In the end, what you select in your project depends on your personal preference and ease of use.
Meticulous Meticulous is a tool for software engineers to catch visual regressions in web applications without writing or maintaining UI tests.
Inject the Meticulous snippet onto production or staging and dev environments. This snippet records user sessions by collecting clickstream and network data. When you post a pull request, Meticulous selects a subset of recorded sessions which are relevant and simulates these against the frontend of your application. Meticulous takes screenshots at key points and detects any visual differences. It posts those diffs in a comment for you to inspect in a few seconds. Meticulous automatically updates the baseline images after you merge your PR. This eliminates the setup and maintenance burden of UI testing.
Meticulous isolates the frontend code by mocking out all network calls, using the previously recorded network responses. This means Meticulous never causes side effects and you don’t need a staging environment.
Learn more here .