Axios vs. Fetch (2025 update): Which should you use for HTTP requests?
Written by Faraz Kelhini✏️ When it comes to making HTTP requests, developers often ask a common question: which is better, Axios or the Fetch API? Axios and the Fetch API help developers handle HTTP/GET/POST/etc. requests in JavaScript. Understanding these technologies' strengths, differences, and use cases is crucial for modern web development. Here are some differences worth noting between the two solutions: Characteristic Fetch API Axios library Origin Native JavaScript API Third-party library Installation Natively available to browsers and Node.js v18+ Requires npm install JSON parsing (see code below) Manual (need to use .json()) Automatic Error handling Minimal (only network errors) Comprehensive Request interceptors Not available (see [this article to implement them in](https://blog.logrocket.com/intercepting-javascript-fetch-api-requests-responses/) `fetch()`) Available Request cancellation (see code below) Requires AbortController Built-in method Response transformation Manual Automatic Platform support Once browser-only but now available in Node.js v18+ Browser and Node.js Some developers prefer Axios over built-in APIs for their ease of use. But many overestimate the need for such a library. The Fetch API is perfectly capable of reproducing the key features of Axios, and it has the added advantage of being readily available in all modern browsers. In this article, we’ll compare fetch() and Axios to see how they can be used to perform different tasks. At the end of the article, you should have a better understanding of both APIs. Editor’s note: This article was updated by Elijah Agnonze in April 2025 to add a decision framework for developers evaluating Axios and the Fetch API, and cover more advanced technical scenarios such as handling timeouts, cancellation requests, and streaming. Understanding the basic syntax of Axios and fetch() Before we delve into more advanced features of Axios, let's compare its basic syntax to fetch(). Here’s how you can use Axios to send a POST request with custom headers to a URL. Axios automatically converts the data to JSON, so you don't have to: // axios const url = 'https://jsonplaceholder.typicode.com/posts' const data = { a: 10, b: 20, }; axios .post(url, data, { headers: { Accept: "application/json", "Content-Type": "application/json;charset=UTF-8", }, }) .then(({data}) => { console.log(data); }); Now compare this code to the fetch() version, which produces the same result: // fetch() const url = "https://jsonplaceholder.typicode.com/todos"; const options = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json;charset=UTF-8", }, body: JSON.stringify({ a: 10, b: 20, }), }; fetch(url, options) .then((response) => response.json()) .then((data) => { console.log(data); }); Notice that: To send data, fetch() uses the body property for a post request to send data to the endpoint, while Axios uses the data property The data in fetch() is transformed into a string using the JSON.stringify method Axios automatically transforms the data returned from the server, but with fetch() you have to call the response.json method to parse the data to a JavaScript object With Axios, the data response provided by the server can be accessed within the data object, while for the fetch() method, the final data can be named any variable Axios and fetch() handle headers in the same way Backward compatibility One of the main selling points of Axios is its wide browser support. Even old browsers like IE11 can run Axios without any issues. This is because it uses [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) under the hood. The Fetch API, on the other hand, only supports Chrome 42+, Firefox 39+, Edge 14+, and Safari 10.1+ (you can see the full compatibility table on CanIUse.com). If your only reason for using Axios is backward compatibility, you don't need an HTTP library. Instead, you can use fetch() with a polyfill to implement similar functionality on web browsers that don’t support fetch(). To use the fetch() polyfill, install it via the npm command like so: npm install whatwg-fetch --save Then, you can make requests like this: import 'whatwg-fetch' window.fetch(...) Keep in mind that you might also need a promise polyfill in some old browsers. Automatic JSON data transformation As we saw earlier, Axios automatically stringifies the data when sending requests (though you can override the default behavior and define a different transformation mechanism). When using fetch(), however, you’d have to do it manually. Compare the two below: // axios axios.get('https://api.github.com/orgs/axios') .then(response => { console.log(response.data); }, error => { co

Written by Faraz Kelhini✏️
When it comes to making HTTP requests, developers often ask a common question: which is better, Axios or the Fetch API?
Axios and the Fetch API help developers handle HTTP
/GET
/POST
/etc. requests in JavaScript. Understanding these technologies' strengths, differences, and use cases is crucial for modern web development.
Here are some differences worth noting between the two solutions:
Characteristic | Fetch API | Axios library |
---|---|---|
Origin | Native JavaScript API | Third-party library |
Installation | Natively available to browsers and Node.js v18+ | Requires npm install |
JSON parsing (see code below) | Manual (need to use .json()) | Automatic |
Error handling | Minimal (only network errors) | Comprehensive |
Request interceptors | Not available (see [this article to implement them in](https://blog.logrocket.com/intercepting-javascript-fetch-api-requests-responses/) `fetch()`) | Available |
Request cancellation (see code below) | Requires AbortController | Built-in method |
Response transformation | Manual | Automatic |
Platform support | Once browser-only but now available in Node.js v18+ | Browser and Node.js |
Some developers prefer Axios over built-in APIs for their ease of use. But many overestimate the need for such a library. The Fetch API is perfectly capable of reproducing the key features of Axios, and it has the added advantage of being readily available in all modern browsers.
In this article, we’ll compare fetch()
and Axios to see how they can be used to perform different tasks. At the end of the article, you should have a better understanding of both APIs.
Editor’s note: This article was updated by Elijah Agnonze in April 2025 to add a decision framework for developers evaluating Axios and the Fetch API, and cover more advanced technical scenarios such as handling timeouts, cancellation requests, and streaming.
Understanding the basic syntax of Axios and fetch()
Before we delve into more advanced features of Axios, let's compare its basic syntax to fetch()
.
Here’s how you can use Axios to send a POST
request with custom headers to a URL. Axios automatically converts the data to JSON, so you don't have to:
// axios
<span class="hljs-title">const</span> url = 'https://jsonplaceholder.typicode.com/posts'
const data = {
a: 10,
b: 20,
};
axios
.post(url, data, {
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
})
.then(({data}) => {
console.log(data);
});
Now compare this code to the fetch()
version, which produces the same result:
// fetch()
const url = <span class="hljs-string">"https://jsonplaceholder.typicode.com/todos"</span>;
const options = {
method: <span class="hljs-string">""hljs-keyword">POST"</span>,
headers: {
Accept: <span class="hljs-string">"application/json"</span>,
<span class="hljs-string">"Content-Type"</span>: "application/json;charset=UTF-8",
},
body: JSON.stringify({
a: "hljs-number">10,
b: "hljs-number">20,
}),
};
fetch(url, options)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
Notice that:
- To send data,
fetch()
uses thebody
property for a post request to send data to the endpoint, while Axios uses thedata
property - The data in
fetch()
is transformed into a string using theJSON.stringify
method - Axios automatically transforms the data returned from the server, but with
fetch()
you have to call theresponse.json
method to parse the data to a JavaScript object - With Axios, the data response provided by the server can be accessed within the data object, while for the
fetch()
method, the final data can be named any variable - Axios and
fetch()
handle headers in the same way
Backward compatibility
One of the main selling points of Axios is its wide browser support. Even old browsers like IE11 can run Axios without any issues. This is because it uses [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
under the hood. The Fetch API, on the other hand, only supports Chrome 42+, Firefox 39+, Edge 14+, and Safari 10.1+ (you can see the full compatibility table on CanIUse.com).
If your only reason for using Axios is backward compatibility, you don't need an HTTP library. Instead, you can use fetch()
with a polyfill to implement similar functionality on web browsers that don’t support fetch()
.
To use the fetch()
polyfill, install it via the npm command like so:
npm class="hljs-keyword">install whatwg-class="hljs-keyword">fetch class="hljs-comment">--save
Then, you can make requests like this:
<span class="hljs-keyword">import</span> 'whatwg-fetch'span>
window.fetch(...)
Keep in mind that you might also need a promise polyfill in some old browsers.
Automatic JSON data transformation
As we saw earlier, Axios automatically stringifies the data when sending requests (though you can override the default behavior and define a different transformation mechanism). When using fetch()
, however, you’d have to do it manually.
Compare the two below:
<span class="hljs-comment">// axios
axios.get(<span class="hljs-string">'https://api.github.com/orgs/axios'</span>)
.then(<span class="hljs-function"><span class="hljs-params">response</span> =>span> {
<span class="hljs-built_in">console</span>.log(response.data);
}, error => {
<span class="hljs-built_in">console</span>.log(error);
});
<span class="hljs-comment">// fetch()
fetch(<span class="hljs-string">'https://api.github.com/orgs/axios'</span>)
.then(<span class="hljs-function"><span class="hljs-params">response</span> =>span> response.json()) <span class="hljs-comment">// one extra step
.then(<span class="hljs-function"><span class="hljs-params">data</span> =>span> {
<span class="hljs-built_in">console</span>.log(data)
})
.catch(<span class="hljs-function"><span class="hljs-params">error</span> =>span> <span class="hljs-built_in">console</span>.error(error));
Automatic data transformation is a nice feature, but again, it's not something you can't do with fetch()
.
HTTP interceptors
One of Axios's key features is its ability to intercept HTTP requests. HTTP interceptors come in handy when you need to examine or change HTTP requests from your application to the server or vice versa (e.g., logging, authentication, or retrying a failed HTTP request).
With interceptors, you won't have to write separate code for each HTTP request. HTTP interceptors are helpful when you want to set a global strategy for how you handle requests and responses.
Here's how you can declare a request interceptor in Axios:
axios.interceptors.request.use(<span class="hljs-function"><span class="hljs-params">(config)</span> =>span> {
<span class="hljs-regexp">// log a message before any HTTP request is sent
<span class="hljs-built_in">console</span>.log("Request was sent"span>);
<span class="hljs-keyword">return</span> config;
});
<span class="hljs-regexp">// sent a GET request
axios.get(<span class="hljs-string">"https://api.github.com/users/sideshowbarker"</span>).thenspan>(<span class="hljs-function"><span class="hljs-params">(response)</span> =>span> {
<span class="hljs-built_in">console</span>.log(response.data);
});
In this code, the axios.interceptors.request.use()
method defines code to be run before an HTTP request is sent. Also, axios.interceptors.response.use()
can be used to intercept the response from the server. Let’s say there is a network error; using the response interceptors, you can retry that same request using interceptors.
By default, fetch()
doesn't provide a way to intercept requests, but it's not hard to come up with a workaround. You can overwrite the global fetch()
method and define your interceptor, like this:
fetch = (<span class="hljs-function"><span class="hljs-params">originalFetch</span> =>span> {
<span class="hljs-keyword">return</span> (...argumentsspan></span>) =>span> {
<span class="hljs-keyword">const</span> result = originalFetch.apply(thisspan>, <span class="hljs-built_in">arguments</span>);
<span class="hljs-keyword">return</span> result.then(consolespan>.log(<span class="hljs-string">'Request was sent'</span>));
};
})(fetch);
fetch(<span class="hljs-string">'https://api.github.com/orgs/axios'</span>)
.then(<span class="hljs-function"><span class="hljs-params">response</span> =>span> response.json())
.then(<span class="hljs-function"><span class="hljs-params">data</span> =>span> {
<span class="hljs-built_in">console</span>.log(data)
});
Download progress with Axios vs. fetch()
Progress indicators are very useful when loading large assets, especially for users with slow internet. Previously, JavaScript programmers used the XMLHttpRequest.onprogress
callback handler to implement progress indicators.
Implementing a progress indicator in Axios is simple, especially if you use the Axios Progress Bar module. First, you need to include the following style and scripts:
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">link</span>
<span class="hljs-attr">rel</span>="stylesheet"span>
<span class="hljs-attr">type</span>="text/css"
"hljs-attr">href="hljs-string">"https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css"
/></span>
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">img</span> idspan>=<span class="hljs-string">"img"</span> /></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> onclickspan>=<span class="hljs-string">"downloadFile()"</span>>span>Get Resource<span class="hljs-tag"></buttonspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> srcspan>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>>span><span class="hljs-tag"></scriptspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> srcspan>=<span class="hljs-string">"https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/index.js"</span>>span><span class="hljs-tag"></scriptspan>></span>
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">style</span>>span> <span class="css"><span class="hljs-selector-id">#nprogress</span> .barspan> {
<span class="hljs-attribute">background</span>: red !importantspan>;
}
<span class="hljs-selector-id">#nprogress</span> .pegspan> {
<span class="hljs-attribute">box-shadow</span>: 0span> <span class="hljs-number">0</span> 10pxspan> red, <span class="hljs-number">0</span> 0span> <span class="hljs-number">5px</span> red !importantspan>;
}
<span class="hljs-selector-id">#nprogress</span> .spinner-iconspan> {
<span class="hljs-attribute">border-top-color</span>: red !importantspan>;
<span class="hljs-attribute">border-left-color</span>: red !importantspan>;
}</span> <span class="hljs-name">style</span>>span>
Then you can implement the progress bar like this:
<span class="hljs-tag"><<span class="hljs-name">script</span> typespan>=<span class="hljs-string">"text/javascript"</span>>span> <span class="javascript">loadProgressBar();
<span class="hljs-function"><span class="hljs-keyword">function</span> downloadFilespan>()</span> {
getRequest(
<span class="hljs-string">"https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg"</span>
);
}
<span class="hljs-function"><span class="hljs-keyword">function</span> getRequestspan>(<span class="hljs-params">url</span>)span> {
axios
.get(url, { <span class="hljs-attr">responseType</span>: "blob"span> })
.then(<span class="hljs-function"><span class="hljs-keyword">function</span> (responsespan>)</span> {
<span class="hljs-keyword">const</span> reader = newspan> <span class="hljs-built_in">window</span>.FileReader();
reader.readAsDataURL(response.data);
reader.onload = <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
<span class="hljs-built_in">document</span>.getElementById("img"span>).setAttribute(<span class="hljs-string">"src"</span>, reader.result);
};
})
.catch(<span class="hljs-function"><span class="hljs-keyword">function</span> (errorspan>)</span> {
<span class="hljs-built_in">console</span>.log(error);
});
}</span> <span class="hljs-name">script</span>>span>
This code uses the FileReader
API to asynchronously read the downloaded image. The readAsDataURL
method returns the image's data as a Base64-encoded string, which is then inserted into the src
attribute of the img
tag to display the image. Alternatively, if you wish to control the pace of the progress bar, Axios provides an onDownloadProgress
event that tracks the progress of your download:
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">link</span>
<span class="hljs-attr">rel</span>="stylesheet"span>
<span class="hljs-attr">type</span>="text/css"
"hljs-attr">href="hljs-string">"https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css"
/></span>
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">img</span> idspan>=<span class="hljs-string">"img"</span> /></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> onclickspan>=<span class="hljs-string">"downloadFile()"</span>>span>Get Resource<span class="hljs-tag"></buttonspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> srcspan>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>>span><span class="hljs-tag"></scriptspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> srcspan>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"</span>>span><span class="hljs-tag"></scriptspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> typespan>=<span class="hljs-string">"text/javascript"</span>>span> <span class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> downloadFilespan>()</span> {
getRequest(
<span class="hljs-string">"https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg"</span>
);
}
<span class="hljs-function"><span class="hljs-keyword">function</span> getRequestspan>(<span class="hljs-params">url</span>)span> {
NProgress.start();
axios
.get(url, {
<span class="hljs-attr">responseType</span>: "blob"span>,
<span class="hljs-attr">onDownloadProgress</span>: (progressEventspan>) =></span> {
<span class="hljs-keyword">const</span> percent = Mathspan>.round(
(progressEvent.loaded * <span class="hljs-number">100</span>) / progressEvent.total
);
NProgress.set(percent / <span class="hljs-number">100</span>);
<span class="hljs-built_in">console</span>.log(`Downloaded ${percent}span>%`);
},
})
.then(function (response) {
NProgress.done();
document
.getElementById("img")
.setAttribute("src", URL.createObjectURL(response.data));
})
.catch(function (error) {
NProgress.done();
console.log(error);
});
} script>
In this example, instead of making use of Axios Progress Bar, which doesn’t provide custom control, we make use of NProgress. This library is what Axios Progress Bar uses behind the hood. It also comes with the ability to customize the style of the progress bar.
On the other hand, the Fetch API doesn't have an onprogress
nor an onDownloadProgress
event. Instead, it provides an instance of ReadableStream
via the body property of the response object.
The following example illustrates the use of ReadableStream
to provide users with immediate feedback during image download:
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">link</span>
<span class="hljs-attr">rel</span>="stylesheet"span>
<span class="hljs-attr">type</span>="text/css"
"hljs-attr">href="hljs-string">"https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css"
/></span>
<span class="hljs-comment"></span>
<span class="hljs-tag"><<span class="hljs-name">img</span> idspan>=<span class="hljs-string">"img"</span> /></span>
<span class="hljs-tag"><<span class="hljs-name">button</span> onclickspan>=<span class="hljs-string">"downloadFile()"</span>>span>Get Resource<span class="hljs-tag"></buttonspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> srcspan>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"</span>>span><span class="hljs-tag"></scriptspan>></span>
<span class="hljs-tag"><<span class="hljs-name">script</span> typespan>=<span class="hljs-string">"text/javascript"</span>>span> <span class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> downloadFilespan>()</span> {
getRequest(
<span class="hljs-string">"https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg"</span>
);
}
<span class="hljs-function"><span class="hljs-keyword">function</span> getRequestspan>(<span class="hljs-params">url</span>)span> {
NProgress.start();
fetch(url)
.then(<span class="hljs-function">(<span class="hljs-params">response</span>) =>span> {
<span class="hljs-keyword">if</span> (!response.ok) {
<span class="hljs-keyword">throw</span> Errorspan>(response.status + <span class="hljs-string">" "</span> + response.statusText);
}
<span class="hljs-comment">// ensure ReadableStream is supported
<span class="hljs-keyword">if</span> (!response.body) {
<span class="hljs-keyword">throw</span> Errorspan>(<span class="hljs-string">"ReadableStream not yet supported in this browser."</span>);
}
<span class="hljs-comment">// store the size of the entity-body, in bytes
<span class="hljs-keyword">const</span> contentLength = response.headers.get("content-length"span>);
<span class="hljs-comment">// ensure contentLength is available
<span class="hljs-keyword">if</span> (!contentLength) {
<span class="hljs-keyword">throw</span> Errorspan>(<span class="hljs-string">"Content-Length response header unavailable"</span>);
}
<span class="hljs-comment">// parse the integer into a base-10 number
<span class="hljs-keyword">const</span> total = parseIntspan>(contentLength, <span class="hljs-number">10</span>);
<span class="hljs-keyword">let</span> loaded = 0span>;
<span class="hljs-keyword">return</span> newspan> Response(
<span class="hljs-comment">// create and return a readable stream
<span class="hljs-keyword">new</span> ReadableStream({
start(controller) {
<span class="hljs-keyword">const</span> reader = response.body.getReader();
read();
<span class="hljs-function"><span class="hljs-keyword">function</span> readspan>()</span> {
reader
.read()
.then(<span class="hljs-function">(<span class="hljs-params">{ done, value }</span>) =>span> {
<span class="hljs-keyword">if</span> (done) {
controller.close();
NProgress.done();
<span class="hljs-keyword">return</span>;
}
loaded += value.byteLength;
NProgress.set(loaded / total);
controller.enqueue(value);
read();
})
.catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =>span> {
<span class="hljs-built_in">console</span>.error(error);
controller.error(error);
});
}
},
})
);
})
.then(<span class="hljs-function">(<span class="hljs-params">response</span>) =>span> {
<span class="hljs-comment">// construct a blob from the data
<span class="hljs-keyword">return</span> response.blob();
})
.then(<span class="hljs-function">(<span class="hljs-params">data</span>) =>span> {
NProgress.done();
<span class="hljs-comment">// insert the downloaded image into the page
<span class="hljs-built_in">document</span>
.getElementById(<span class="hljs-string">"img"</span>)
.setAttribute(<span class="hljs-string">"src"</span>, URL.createObjectURL(data));
})
.catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =>span> {
NProgress.done();
<span class="hljs-built_in">console</span>.error(error);
});
}</span> <span class="hljs-name">script</span>>span>
Making simultaneous requests
To make multiple, simultaneous requests, Axios provides the axios.all()
method. Simply pass an array of requests to this method, then use axios.spread()
to assign the properties of the response array to separate variables:
axios.all([
axios.get(<span class="hljs-string">'https://api.github.com/users/iliakan'</span>),
axios.get(<span class="hljs-string">'https://api.github.com/users/taylorotwell'</span>)
])
.<span class="hljs-keyword">then</span>(axios.spread((obj1, obj2) => {
<span class="hljs-comment">// Both requests are now complete
console.<span class="hljs-built_in">log</span>(obj1.dataspan>.login + <span class="hljs-string">' has '</span> + obj1.dataspan>.public_repos + <span class="hljs-string">' public repos on GitHub'</span>);
console.<span class="hljs-built_in">log</span>(obj2.dataspan>.login + <span class="hljs-string">' has '</span> + obj2.dataspan>.public_repos + <span class="hljs-string">' public repos on GitHub'</span>);
}));
You can achieve the same result by using the built-in Promise.all()
method. Pass all fetch
()
requests as an array to Promise.all()
. Next, handle the response by using an async
function, like this:
<span class="hljs-built_in">Promise</span>.all([
fetch(<span class="hljs-string">'https://api.github.com/users/iliakan'</span>),
fetch(<span class="hljs-string">'https://api.github.com/users/taylorotwell'</span>)
])
.then(<span class="hljs-keyword">async</span>([res1, res2]) => {
<span class="hljs-keyword">const</span> a = awaitspan> res1.json();
<span class="hljs-keyword">const</span> b = awaitspan> res2.json();
<span class="hljs-built_in">console</span>.log(a.login + ' has 'span> + a.public_repos + <span class="hljs-string">' public repos on GitHub'</span>);
<span class="hljs-built_in">console</span>.log(b.login + ' has 'span> + b.public_repos + <span class="hljs-string">' public repos on GitHub'</span>);
})
.catch(<span class="hljs-function"><span class="hljs-params">error</span> =>span> {
<span class="hljs-built_in">console</span>.log(error);
});
Effectively handling responses
Response management is a critical part of every application invoking an API. In this section, we will briefly look at the two aspects of it: getting the error code and manipulating response data.
Error management is different in Axios and the Fetch API. Specifically, fetch()
doesn’t automatically reject the promise
in the event of server-side errors, such as HTTP 404 or 500 status codes. This means that these errors don’t trigger the .catch()
block, unlike in Axios, where such responses would typically be considered exceptions.
Instead, fetch()
will resolve the promise
normally with the ok
status in the response set to false
. The call to fetch()
will only fail on network failures or if anything has prevented the request from completing.
In the following code, you can see how to handle errors in fetch()
:
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> res = awaitspan> fetch(<span class="hljs-string">'...'</span>);
<span class="hljs-keyword">if</span> (!res.ok) {
<span class="hljs-comment">// Error on the response (5xx, 4xx)
<span class="hljs-keyword">switch</span> (res.status) {
<span class="hljs-keyword">case</span> 400span>: <span class="hljs-comment">/* Handle */</span> breakspan>;
<span class="hljs-keyword">case</span> 401span>: <span class="hljs-comment">/* Handle */</span> breakspan>;
<span class="hljs-keyword">case</span> 404span>: <span class="hljs-comment">/* Handle */</span> breakspan>;
<span class="hljs-keyword">case</span> 500span>: <span class="hljs-comment">/* Handle */</span> breakspan>;
}
}
<span class="hljs-comment">// Here the response can be properly handled
} <span class="hljs-keyword">catch</span> (err) {
<span class="hljs-comment">// Error on the request (Network error)
}
Meanwhile, in Axios, you can discriminate all errors in a proper catch
block as shown in the following example:
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">let</span> res = awaitspan> axios.<span class="hljs-keyword">get</span>('...'span>);
<span class="hljs-comment">// Here the response can be properly handled
} <span class="hljs-keyword">catch</span> (err) {
<span class="hljs-keyword">if</span> (err.response) {
<span class="hljs-comment">// Error on the response (5xx, 4xx)
} <span class="hljs-function"><span class="hljs-keyword">else</span> ifspan> (<span class="hljs-params">err.request</span>)span> {
<span class="hljs-comment">// Error on the request (Network error)
}
}
Once the request has been served with a proper response without any errors, you can handle the response payload that will be accessible by using two different mechanisms.
In fetch()
, the request/response payload is accessible in the body
field and must be stringified, while in Axios it is in the data
field as a proper JavaScript object. This difference is captured in the following, stripped-down examples:
<span class="hljs-comment">// Using Fetch API
fetch(<span class="hljs-string">'...'</span>)
.then(<span class="hljs-function"><span class="hljs-params">response</span> =>span> response.json())
.then(<span class="hljs-function"><span class="hljs-params">data</span> =>span> <span class="hljs-built_in">console</span>.log(data))
.catch(<span class="hljs-function"><span class="hljs-params">error</span> =>span> <span class="hljs-built_in">console</span>.error('Error:'span>, error));
<span class="hljs-comment">// Using Axios
axios.get(<span class="hljs-string">'...'</span>)
.then(<span class="hljs-function"><span class="hljs-params">response</span> =>span> <span class="hljs-built_in">console</span>.log(response.data))
.catch(<span class="hljs-function"><span class="hljs-params">error</span> =>span> <span class="hljs-built_in">console</span>.error('Error:'span>, error));
The key difference in fetch()
lies in the use of the .json()
method. Despite the name, this method does not produce JSON. Instead, it will take JSON as an input and parse it to produce a JavaScript object.
Advanced use cases of Axios vs Fetch
In this section, we will look into some advanced use cases of Axios and fetch()
, like handling response timeouts, cancelling requests, and streaming requests. You’ll often need these features in real-world applications.
Handling response timeouts
The simplicity of setting a timeout in Axios is one of the reasons some developers prefer it to fetch()
. In Axios, you can use the optional timeout
property in the config object to set the number of milliseconds before the request is aborted.
Here’s an example:
axios
.get(
<span class="hljs-string">"https://overpass-api.de/api/interpreter?data=\[out:json];way[highway\](40.5,-74,41,-73.5);out qt 30000;"</span>,
{ timeout: <span class="hljs-number">4000</span> }
)
.<span class="hljs-keyword">then</span>((response)span> =></span> {
<span class="hljs-built_in">console</span>.log(response);
})
.<span class="hljs-keyword">catch</span>((error)span> =></span> consolespan>.error(<span class="hljs-string">"timeout exceeded"</span>));
fetch()
provides similar functionality through the AbortController
interface. However, it's not as simple as the Axios version:
const controller = new AbortController();
const options = {
method: "GET",
signal: controller.signal,
};
const promise = fetch(
"https://overpass-api.de/api/interpreter?data=\["hljs-string">out:json];way["hljs-string">highway\]("hljs-link">40.5,-74,41,-73.5);out qt 30000;",
options
);
const timeoutId = setTimeout(() => controller.abort(), 4000);
promise
.then((response) => {
<span class="hljs-code">console.log(response);</span>
})
.catch((error) => console.error("timeout exceeded"));
Here, we created an AbortController
object which allows us to abort the request later using the abort()
method. Signal
is a read-only property of AbortController
, providing a means to communicate with a request or abort it. If the server doesn’t respond in less than four seconds, controller.abort()
is called, and the operation is terminated.
Cancelling requests
As we’ve just seen, we can make use of the abort()
method of AbortController
to cancel requests made with the Fetch API:
const controller = <span class="hljs-keyword">new</span> AbortController();
const getRequest = <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
const options = {
method: <span class="hljs-string">"GET"</span>,
signal: controller.signal,
};
fetch(
<span class="hljs-string">"https://overpass-api.de/api/interpreter?data=\[out:json];way[highway\](40.5,-74,41,-73.5);out qt 10000;"</span>,
options
)
.<span class="hljs-keyword">then</span>((response)span> =></span> {
<span class="hljs-built_in">console</span>.log(response);
})
.<span class="hljs-keyword">catch</span>((error)span> =></span> consolespan>.error(error));
};
const cancelRequest = <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
controller.abort();
};
In Axios, an optional cancelToken
property in the config object is provided to allow cancelling requests:
const source = axios.CancelToken.source();
const getRequest = <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
axios
.get(
<span class="hljs-string">"https://overpass-api.de/api/interpreter?data=\[out:json];way[highway\](40.5,-74,41,-73.5);out qt 10000;"</span>,
{
cancelToken: source.token,
}
)
.<span class="hljs-keyword">then</span>((response)span> =></span> {
<span class="hljs-built_in">console</span>.log(response);
})
.<span class="hljs-keyword">catch</span>((error)span> =></span> consolespan>.error(error));
};
const cancelRequest = <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
source.cancel();
};
Streaming
We’ve been introduced to streaming when we looked at download progress. But Axios by default does not support streaming in the browser. The onDownloadProgress
is the closest thing to streaming in the browser, but outside the browser, we can set a responseType
of stream
to use:
function streamLargeData() {
<span class="hljs-regexp">// Store the complete response as it comes in
let responseData = <span class="hljs-string">""</span>;
axios
.get(
<span class="hljs-string">"https://overpass-api.de/api/interpreter?data=\[out:json];way[highway\](40.5,-74,41,-73.5);out qt 50000;"</span>,
{
responseType: <span class="hljs-string">"stream"</span>,
}
)
.<span class="hljs-keyword">then</span>((response)span> =></span> {
<span class="hljs-regexp">// Handle the data stream
response.data.<span class="hljs-literal">on</span>("data"span>, <span class="hljs-function"><span class="hljs-params">(chunk)</span> =>span> {
const chunkData = chunk.toString();
responseData += chunkData;
<span class="hljs-built_in">console</span>.log(`Received chunk ofspan> size: ${chunk.length} bytes</span>`);
});
<span class="hljs-regexp">// Handle stream completion
response.data.<span class="hljs-literal">on</span>("end"span>, <span class="hljs-function"><span class="hljs-params">()</span> =>span> {
const parsedData = JSON.parse(responseData);
<span class="hljs-built_in">console</span>.log(parsedData);
});
})
.<span class="hljs-keyword">catch</span>((error)span> =></span> consolespan>.error(error));
}
For fetch()
, we can use the ReadableStream
from the response, as we saw earlier in the Download Progress section.
When should you use Axios vs Fetch API?
One of the deciding factors for choosing between Axios and Fetch the Fetch API is the nature of your project. Here are some common things your project may require and the best choice for it:
Use case | Best choice |
---|---|
Simple browser requests | Fetch API (native, lightweight) |
Large-scale API calls | Axios (streaming, interceptors, timeouts) |
Built-in support in modern frameworks | Fetch API (default in browser and Node.js v18+) |
Retry logic & fault tolerance | Axios (throws for non 2xx errors, built-in timeouts) |
File uploads/downloads | Axios (built-in progress events) |
Building a reusable HTTP utility | Axios (flexibility, and built-in functionalities) |
Authenticated requests & token management | Axios (built-in interceptors) |
Axios vs. Fetch: What do developers say?
Developers are big believers in, "if it isn’t broken, don't fix it". Devs who have been in big companies with thousands of lines of code wouldn't want to change something that works fine.
As reflected in a Reddit discussion on Axios vs Fetch, this is how most developers feel about switching from Axios to Fetch:
- Error handling — Many developers prefer Axios to Fetch because throwing errors on 4xx and 5xx status codes comes by default. On the other hand, Fetch requires a manual check, as we've seen earlier
- Ease of use — The built-in functionalities of Axios, like auto-parsing JSON, interceptors, timeouts, etc, make it a constant go-to for many developers. As a developer highlighted: "I use it (Axios) for the interceptors feature, which afaik fetch doesn’t have. Sure I could build my own but then I’d have to build my own"
- Backward compatibility — Axios is backward-compatible with IE11 (which some devices still use today). But that doesn't rule out Fetch as polyfills make it backward-compatible
- Performance considerations — Fetch has a smaller bundle size, even with a polyfill, and does not require additional abstractions that reduce processing time
- Flexibility — Axios supports sending body with a
GET
request, whereas Fetch is stuck with the adherence to HTTP standards, even though there are a few developers demanding this feature since 2015
Frequently asked questions
Why do developers prefer Axios over fetch()
?
Developers often prefer to stick with what already works without problems. The major drawback to Axios is its status as a dependency, and that it’s not causing problems for most developers. Hence why they'd rather stick to that.
Is fetch()
better for performance than Axios?
Yes. Fetch is a native browser API, which makes it lighter and more efficient. It has a smaller bundle size (even with a polyfill), runs directly in the browser or Node.js without additional abstractions, and supports streaming responses on the browser, unlike Axios.
Will fetch()
fully replace Axios?
No. Axios is just like jQuery in terms of how long it has been helpful to developers. Aside from that, its constant maintenance shows it will be here for a while. About a year ago, Axios added support for Fetch in version 1.7. This means you can choose to use the Fetch API instead of the default XMLHttpRequest
in Axios.
How to configure CORS
Cross-Origin Resource Sharing (CORS) is a mechanism available in HTTP to enable a server to permit the loading of its resources from any origins other than itself. For example, you need CORS when you want to pull data from external APIs that are public or authorized.
If the CORS mechanism is not properly enabled on the server, any request from a different server — regardless of whether or not it is made with Axios or fetch()
— will receive the No Access-Control-Header-Present
error.
To properly handle CORS, the first step is to configure the server, which depends on your environment/server. Once the server has been properly configured, it will automatically include the Access-Control-Allow-Origin
header in response to all requests (see the documentation for more information).
A common error, in both Axios and fetch()
, is to add the Access-Control-Allow-Origin
to the request — this is a response parameter and is used by the server to specify the permitted access control for the origin.
Another aspect to be aware of, when you add the headers to your Axios request, is that the request is handled differently. The browser performs a preflight request before the actual request. This preflight request is an [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS)
request that verifies if CORS is honored, and if the actual request is safe to send the real request.
Conclusion
Axios provides an easy-to-use API in a compact package for most HTTP communication needs. However, if you prefer to stick with native APIs, nothing is stopping you from implementing Axios features.
As discussed in this article, it's possible to reproduce the key features of the Axios library using the fetch()
method provided by web browsers. Whether it’s worth loading a client HTTP API depends on whether you’re comfortable working with built-in APIs.
LogRocket: Debug JavaScript errors more easily by understanding the context
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!