You don't need Axios, well, for most use cases. The web is maturing, and the days of Axios are ending.
Why we needed Axios
For newcomers, not long ago, we needed to use XMLHttpRequest
to fetch data from servers. To fetch data from https://jsonplaceholder.typicode.com/todos/, we would have needed the following lines of code at the very minimum.
const req = new XMLHttpRequest();
req.addEventListener("load", function() {
console.log(JSON.parse(this.responseText))
})
req.open('GET', 'https://jsonplaceholder.typicode.com/todos/')
req.send()
We needed an elegant abstraction over XMLHttpRequest, and axios
filled that space perfectly, especially with the introduction of Promise in ES6.
With axios
, we can achieve the same thing with just one line of code
const data = (await axios.get('https://jsonplaceholder.typicode.com/todos/')).data
But time has passed, the web has matured, and now, we have fetch()
Use fetch() Instead
With fetch, you get most of the benefits of Axios with zero external dependencies in your application. I am a proponent of minimal dependencies. If the platform I'm on provides a functionality, and it's relatively easy to implement, I'll explore it first. This is even more important with JavaScript runtimes, as every abstraction comes at a cost.
GET Request Using Fetch
const data = await fetch('https://jsonplaceholder.typicode.com/todos/')).then(res => res.json())
POST Request Using Fetch
The fetch API takes in a second parameter, which allows you to specify other options, including your HTTP method, headers, and body. The example below shows how to make a post request using fetch API. This example also applies to other HTTP methods that mutate the server;
const data = await fetch('https://jsonplaceholder.typicode.com/todos/', {
method: 'POST',
body: JSON.stringify({title: 'buy milk'})
}).then(res => res.json())
Uploading Files
Assuming you have the following HTML file
<!Docktype html>
<html lang=en>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width initial-scale=1.0">
<meta http-eqiv="X-UA-Compatibly" content="ie=edge">
<title>File Upload</title>
</head>
<body>
<form id="fileform" enctype="multipart/form-data">
<input name="upload" type="file" placeholder="upload file" />
<button type="submit">submit</button>
</form>
<script src="./index.js"></script>
</body>
</html>
then your fetch call would look like this:
const form = document.getElementById("fileform")
form.addEventListener("submit", (event) => {
event.preventDefault();
const fileInput = document.getElementsByName('upload')[0]
const fd = new FormData()
fd.append('upload', fileInput.files[0]);
fetch('http://localhost:5001/upload', { method: 'POST', body: fd}).then(res => res.json().then(body => console.log('result', body)))
})
Pro Tip
Always wrap your fetch function in with another function; This gives you leeway for abstracting HTTP call patterns emerging as your application grows. See the code snippet below.
const originalFetch = window.fetch
window.fetch = async (...args) => {
// intercept request
const res = originalFetch(...args)
// intercept response e.g
if(res.ok) {
return res.json()
} else
//Do whatever you want to handle the error
}
}
The Whataboutisms
What about Request and Response Interceptors
We've talked about that briefly in the previous subheading
const interceptRequest = (...args) => {
// perform some magic
return args
}
const interceptResponse = (res) => {
// perform some magic
return res;
}
const originalFetch = window.fetch
window.fetch = async (...args) => {
const modifiedArgs = interceptRequest(...args)
const res = await orginalFetch(modifiedArgs)
return interceptResponse(res)
}
Edit 24 Oct 2023 More On Intercepting Requests
Due to questions from colleagues, I realized I needed to address this a little more.
To intercept means to stop something from proceeding. Interceptors in HTTP libraries(you could call them middleware), stop requests and modify the request configuration using your provided function. For response, interceptors take raw server responses and modify them based on your logic before returning them to the library consumers.
These can be implemented as simple functions that get called before and after your call to fetch
Axios and other JS libraries don't do magic with your interceptors. They only call the functions you give to them and call them for you
What About Timeouts
Timeouts should primarily be the business of your backend. It would help if you also had a backend that integrates the third-party platforms, but should you choose to incorporate an unreliable backend with your frontend, you can still implement timeouts with fetch API so
// timeout request after 5 seconds
const res = await fetch('https://jsonplaceholder.typicode.com/todos/', { abort: AbortSignal.timeout(5000) })
What About Javascript In The Backend, Aka Nodejs/Deno/Bun
The three significant runtimes now implement fetch, and if you must install any HTTP client for your javascript backend, install undici.
Undici is an HTTP client built from the ground for nodejs, and the nodejs organization maintains it. Undici also implements fetch API
What about a progress update for large file uploads
Use XMLHttpRequest ;). Unfortunately, fetch() has yet to include this feature.
Conclusion
Regarding dependencies, I follow the YAGNI principle in extreme programming. There is no need to bloat your application with thousands of dependencies. The web is maturing, and like JQuery, axios' time is gradually ending. With current developments, Axios and other popular HTTP client libraries are no longer required to have good developer experience.
Top comments (35)
You may not need axios, just like you do not need any luxuries in life.
But why would you avoid
axios
?Axios provides a much better api / dev experience calling http endpoints. That's a plus.
What are the downsides, except bundle size ?
axios bundle size is 57 kB -> 21.8 kB (gzip)
bundlejs.com/?q=axios%401.5.1
Hi @adaptiveshieldmatrix
Thanks for your feedback. I agree with you on the DX part.
I know the article says "axios" but it's really about not bringing in dependencies unless the DX is so poor you'd throw your laptop out if you don't bring in a library.
Picking on
axios
one more time: axios have three dependencies, one of which is form-data. We don't need this dependency in the browser because the browser natively supports form data, and you also don't need it in nodejs from node 18+. Form data also have 3 other dependencies that also have their dependencies. The dependency tree goes on and on, and at any point, one of the maintainers might decide to hit the kill switch on the project. For example, form-data's last update was three years ago, but it currently has 113 open issues.All that dependency when what most(not all) web applications out there do is fetch data and map to JSX or render to HTML, listen to events, submit data, update state, and repeat.
1.
I think its okay for library to not get any updates if the protocol (multipart, like in the case of form-data) is stable.
Do all libraries have to release new versions just for appearance sake?
Axios has many issues listed
github.com/axios/axios/issues?q=is...
but no bugs (mostly feature requests and questions about edge cases)
and seems very safe / stable to use.
I can understand your strife to reduce dependencies, but that are the alternatives ? Write http data fetching libraries yourself
or suffer poor DX because of raw XMLHttpRequest / fetch.
2.
I have looked a bit more at http client libraries
Here seems to be a good comparison
github.com/sindresorhus/got#compar...
Following your main grip against axios -> ky seems to be the next best choice for http clients in the browser
github.com/sindresorhus/ky
With a bundle size of only 9.12 kB -> 3.23 kB gzip
https://bundlejs.com/?q=ky%401.1.0&treeshake=%5B*%5D
thanks. Would check the links out
One reason to avoid axios, difficult to handle errors.
How is it more difficult to handle than fetch?
You can even overwrite/define then it throws errors
pipedream.com/community/t/faq-how-...
it's not about when it throws, it's how it handles errors. It hides the response in error.response.data, which becomes a big issue when dealing with typescript.
How is accessing the response with
error.response.data
in typescript a big problem?When using
asyn/await
you loose all types, so you have to doAnd I just don't like writing
error.response?.data?.whatever
in general. I generally prefer dealing withResponse
.And there's no way to conditionally tell axios to not parse responses. One big reason to not use axios is because of the all or nothing behaviour. You have build a client for each scenario (which is unrealistic)
You can type axios, example:
await axios.post<EventType[] | { errorMessage: string }>(urlApiCmdPublic, cmd)
so I would not count that as a downside. Fetch in comparison does not have any types at all.
"no way to conditionally tell axios to not parse responses" - that's a fair point,
but its relevant only if using some non-http/rest APIs - which to be fair should be pretty rare.
I agree with you, that
axios
is mostly designed to consume http/rest apis with good developer experienceand having the niche use-case of calling lower level binary apis is best served using lower level tools like
fetch
.await axios.post<EventType[] | { errorMessage: string }>(urlApiCmdPublic, cmd)
only works for non-error responses.Not exactly true, you get a
Response
which makes sense. And then you can convert to json and do validation i.e using zodschema.parse(await response.json())
to get a concrete type. Which is a much better approach than what axios offersI would be very interested in seeing exactly what you refer to. What am I gaining, exactly, for the extra 57kb?
The top things I can think of:
json()
(in comparison to fetch) to get the dataWith the release of
fetch
this list got much shorter...I think I'm slowly coming around to using fetch on the browser as well
I see. I think the author (and I agree) is on the line of "90% of people don't need adapters, don't need base paths or have them covered elsewhere". I think that for 9 out of 10 developers,
axios
is 57kb of "nothing I need".I imagine that the adapters are probably very handy, but are also probably very specific and catered for the minority, not the majority.
So yes, I think you are doing the right thing considering
fetch()
as the primary tool here. If you need interception of request or response, that's something that can be easily done in far less than 57kb.Cheers!
Its size is 13.7 kB gzipped (ESM)
18.8 kB gzipped (UMD, ES5)
Never Mutate Global State
I suggest that mutating global state like this is the opposite of a pro tip:
Now, every single client side request that uses fetch under the hood will not work as expected. You've literally broken every request.
In a commercial setting, you're almost guaranteed to be running some vendor scripts on the client. Things like logging services, tracking and marketing, A|B tests, heat mapping or service integrations. All of these will no longer work as expected if they use the
fetch
API. Not to mention any browser extensions or user scripts that may be loaded on the client...Even the simple action of transforming the response to JSON like this will break any call to
fetch
.Broken Implementation
Just try this example after mutating
global.fetch
:Calling this function will output:
This is incredibly difficult to reason about as these errors are misleading.
In this case, vendors who use
axios
are better off because this mutation doesn't affectXMLHttpRequest
.Use Encapsulation
This is closer to what I would consider a professional approach:
Neither Choice is Superior
The choice between
axios
andfetch
depends on the context of the problem. You've laid out a number of examples that, from your perspective, makeaxios
seem redundant to you. However, your examples actually make a strong case for usingaxios
in a commercial setting to me.As someone who's created some
XMLHttpRequest
implementations by hand, I have similar misgivings to usingfetch
today that I did back then.It really highlights the convenience that
axios
provides. I look at your form examples and I see a lot of additional code that the team has to maintain.Your example has zero dependencies:
This example is less complex, more convenient and less maintenance:
Reinventing the Wheel vs Getting Things Done
I want to reiterate that I don't see any choice as superior to the other before sharing my experience using fetch out of the box in a project.
It almost always ends up with something like this to make a basic request:
Then a stakeholder comes along and adds a new business requirement to fetch something from
resource-b
as well. So the fetch logic is encapsulated:This is the reality that you face when building complex applications with
fetch
. It's a considerable investment to save 23KB on a request.To achieve the same result with
axios
:On Not Mutating Global State
That is an excellent point on not mutating the global state. Thanks for the callout. The main point I was trying to make is to
always encapsulate(wrap)
.In my codebase, it looks something like this.
I'll do an edit sometime during the day to reflect this.
To reinvent the wheel or not to
This topic is very subjective, so I'll have to answer subjectively. Having written and made HTTP requests in another language (i.e. go and rust) in the past year, returning to
fetch
didn't seem like a lot of work.Arguably, many of the JS libraries out there are reinventing the wheel for better or worse.
At the end of the day, it's up to your team to decide how much re-invention is too much reinvention.
On whether one is superior to the other
This is not a subjective one, so here are my plus for
fetch
I used to have this opinion that
axios
wasn't worth using overfetch
. But lately I've found that it has some nice middleware features thatfetch
doesn't have. So I'd say it isn't worth throwing out just yet.I guess it all just depends on how you use it in the end. If you're not using some of those special
axios
features then by all means don't add it as a dependency to your project when you can just usefetch
.I completely agree with you that some large-scale projects especially the ones that require heavy file uploads will still benefit much from axios.
Earlier this year, I took over a project and I was kindof irritated by the fact that the original developer used just fetch. I thought it would end up being a problem for me along the line but after many months, I completely stopped missing axios. This is what inspired this article.
Ok yeah that makes a lot of sense! Thanks for sharing :)
I followed your recommendation and took a look at
undici
There is a long discussion about fetch performance
github.com/nodejs/undici/issues/1203
For example in this benchmark (updated about a month ago)
github.com/silverwind/fetch-bench
undici: 2966ms
axios: 2429ms
-> axios is faster than undici
The increased bundle size of axios (57 kB -> 21.8 kB gzip) is not an issue on the server (only in the client/browser)
I would say, that it is a contested point, which of the 2 libraries is better
axios
orundici
Interesting. I'm watching that space now to see how it evoves overtime. Thanks for sharing. We learn everyday.
Hey thanks, nice and concise explanation. I am a proponent of ditching all JS Libraries for most use cases. Without JQuery or Axios we can now do everything in Plain Vanilla JS. Fetch is definately the way to go. Also I ditched my databases too and am now all serverless using json for my small use case and non relational data sets. PS, I am not a student so can get away without using React or Node. I use only small apps for everything, like AWS Lambdas for example. Everything as a Service !!
Timeouts are not only backend related. We could have a connection timeout without even reaching the backend, or a receive timeout when the network connection drops.
Thanks but Axios saved one of my projects. However you bring up a valid point with Fetch giving you the option to not have any dependencies in your project. I had issues with Fetch before but I will give it a crack again sometime.
Thanks Matthew. Contrary to what it might sound like, I'm not advocating against using axios. I'm just raising awareness that we now have this cool thing called fetch and it's getting really good. π
Yes, i also often think it is much easier to wrap the fetch api than learning the api of axios.
The AbortSignal Timeout was new to me π
But Axios is more then getting data, itβs also well structured Error handling and developer experience (DX)
Iβm a big fan of axios, but I am not offended by this π very nice write up. ππ»
Some comments have been hidden by the post's author - find out more