Some practical examples on why I prefer Axios over Fetch
Tags: javascript, webdev, api
Published: 2024-02-06 13:54:37

I want to be clear from the very start, this is not a post of why "you should always avoid fetch", as for what you do and you know the limitations and how to get around it, and you are happy with that, that's totally ok for me.

But, I often see comments across our team where people ask why we use `axios`, a library to fetch data from a resource, then using the native [`fetch` library](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
And to be fair is a totally valid question in my opinion.

We could argue on the advantages on using one instead of the other and vice-versa, like you don't need any dependencies for `fetch` (unless you need to support old browsers and use a polyfill) or the other can give you a better degree of tuning, but I'm a practical person and I prefer to show practical examples, and let the code explain why I prefer X over Y.

Let's have this simple case, where you have to fetch some data from an API, and I want to check if the request is successful or not.
Normally I prefer to wrap calls like this into a `try..catch` statement, so I can catch the error if the request fails:

```javascript
const axios = require('axios');

const TEST_ENDPOINT = 'https://somedomain.com/api/someendpoint';

(async () => {
  try {
    await fetch(TEST_ENDPOINT);
    console.log('using fetch: all good');
  } catch (error) {
    console.error('using fetch: error');
  }
})();

(async () => {
  try {
    await axios.get(TEST_ENDPOINT);
    console.log('using axios: all good');
  } catch (error) {
    console.error('using axios: error');
  }
})();
```

My expectation would be that they both act the same, if the request is successful, I'll se the `all good` message, but if the API returns anything else than a `200 OK` status header, it should throw an error.

But if you run the code above, this will happen:
```sh
using fetch: all good
using axios: error
```

Why with `fetch` I get the `all good` message?

That's because `fetch` **doesn't throw any error if the request fails**, it will just rely on the code to handle that by looking at the response from the request, using either the `status` or `ok` properties,
while with `axios` if a request fails, it will throw an error immediately.
So to make your `fetch` request works properly, you will need to add some extra code:

```javascript
(async () => {
  try {
    const result = await fetch(TEST_ENDPOINT);
    if (!result.ok) {
      throw new Error('Not found');
    }
    console.log('using fetch: all good');
  } catch (error) {
    console.error('using fetch: error');
  }
})();
```

Again, if you are ok to add this extra check, I'm totally ok to use `fetch`.

Now, let's say that we are expecting the response to be a JSON object, and we want to use that in our code.
With `axios`, you just need to fetch the `data` property from the axios response, and it's already a JSON object, no need to do any transformation or call any functions, **it's ready to go**.

With `fetch` you have to get the response from the request, and use the `json()` function to transform the raw data into a JSON object.
When you add with the previous error checking, to do the same exact job, you will have your code that will look like this:

```javascript
// Fetch
const response = await fetch(TEST_ENDPOINT);
if (response.status !== 200) {
  throw new Error('Not found');
}
const data = await response.json();

// Axios one liner
const { data } = await axios.get(TEST_ENDPOINT);
```

The code above does the same exact thing, but with `axios` is one line of code, while with `fetch` is 5.
And when you build large applications, it can become tedious to keep adding the same controls, unless you create your own wrap function, which again is more code that you have to maintain.

Is this a deal breaker for you? Maybe not, maybe you do just an handful of requests in your application, and that's totally fine to use `fetch`, especially if you don't want to have more dependencies.

What about doing requests that don't use the `GET` method?

Let's say I want to send some data to an API? 
With `fetch` you will have something like this:

```javascript
const yourPostData = {
  name: 'Alessio',
  age: 25,
};

const response = await fetch('https://example.com/profile', {
  method: 'POST',
  headers: {
	'Content-Type': 'application/json',
  },
  body: JSON.stringify(yourPostData),
});

const result = await response.json();
```

So you have to pass an object, telling `fetch` to use the `POST` method, transform the data into a string, and pass the headers to tell it to encode it as `application/json`.
Then to read the response we'll need to transform the data back to JSON.

The same above with `axios` is again, excluding the input data, a one liner code:

```javascript
const yourPostData = {
  name: 'Alessio',
  age: 25,
};

const { data } = await axios.post('https://example.com/profile', yourPostData);
```

Personally I prefer to have brevity of code, at the expense of a small dependency. Again, it's more than that, these are just two simple examples to explain my point in the argument, and if you are ok to write more verbose code, or create your own wrapper, that's totally ok.