【JavaScript】axiosがfetchより使いやすいと思う理由

JavaScriptでAPIを叩く際に使用する代表的な武器として、fetchとaxiosが挙がると思います。

fetchはJavaScriptの標準ライブラリなのに対し、axiosはインストールする必要があります。

それでも僕がaxiosがfetchより使いやすいという理由を、ざっくりと両者の特徴を紹介しながらいくつか述べてみました!

サンプルのAPIとして、猫の雑学(英語)をランダムで返してくれる[catfact.ninja](https://catfact.ninja/)をお借りしています😺
~~~
https://catfact.ninja/fact
~~~

---
### 1. エラーハンドリング

#### 【fetchの場合】
fetchは、`サーバのレスポンスが届きはじめた時点でPromiseを解決する`という特徴があります。つまり、サーバのレスポンスが届きさえすれば、とりあえずPromiseの`resolve()`が実行されてしまうということです。

これが、若干不便です。
400エラーや500エラーが起きた場合でも、`then`の中が実行されてしまうのです。

たとえば、以下のようなプログラムがあったとします。
~~~javascript
fetch('https://catfact.ninja/foo').then((response) => {
  console.log(response);
  alert('Success!');
}).catch((error) => {
  console.error(error);
});
~~~

`https://catfact.ninja/foo`というURLは存在しないので、404ステータスが返るのですが、このプログラムを実行すると、ブラウザに「Success!」というアラートが出ます。


ステータスがどうであれ、サーバー(catfact.ninja)にリクエストが飛んでレスポンスを返しているのでPromiseは`resolve`を実行し、`then`の中の処理が実行されたという訳です。

以下が404ステータスが返った場合のResponseオブジェクトの内容になります。
~~~php
{
  body: ReadableStream,
  bodyUsed: false,
  headers: Headers,
  ok: false,
  redirected: false,
  status: 404,
  statusText: "",
  type: "cors",
  url: "https://catfact.ninja/foo"
}
~~~


fetchが返すResponseオブジェクトには、`ok`というプロパティがあります。（上記4つ目のプロパティ）

レスポンスのステータスが200~299の場合、`ok`プロパティが`true`になり、それ以外のステータスの場合は`false`になります。

この`ok`プロパティを利用して`then`の中に分岐を入れてあげることで、200~299以外のステータスの場合のハンドリングが可能になります。

とはいえ、エラーが発生しているのに`then`の中が実行されることに、僕は少しだけ違和感を感じちゃいます...
~~~javascript
fetch('https://catfact.ninja/foo').then((response) => {
  if (response.ok) {
    console.log(response);
    alert('Success!');
  } else {
    alert('Error!');
  }
}).catch((error) => {
  console.error(error);
});
~~~

ちなみに、200~299以外でも`then`が実行されるのなら、`catch`はどのような場合に実行されるのかと、疑問に思う方もいると思います。
fetchにおいて、`catch`はレスポンスがない場合、つまり通信状況の不具合などでHTTP通信そのものが失敗した場合に実行されます。

#### 【axiosの場合】

axiosでは、200~299以外のHTTPステータスがレスポンスで返ってきた場合には`catch`が実行されます。
なので、以下のプログラムを実行すると、サーバーから404レスポンスが返り、`catch`が実行されブラウザに`Error!`というアラートが表示されます。

~~~javascript
axios.get('https://catfact.ninja/foo').then((response) => {
  alert('Success!');
}).catch((error) => {
  alert('Error!');
});
~~~


---

### 2. GETメソッド以外の通信

#### 【fetchの場合】
fetchを使用する場合、GET以外のメソッドのHTTP通信を行う場合には、以下のように実行時の第二引数のプロパティ`method`でHTTPメソッドを指定することでPOST通信などが可能になります。

また、リクエストボディを設定する際は、第二引数のプロパティに`body`に`JSON.stringify`でJSON形式にフォーマットしたオブジェクトを設定します。

~~~javascript
fetch('api/v1/users', {
  method: 'POST',
  body: JSON.stringify({
    name: 'Whopper',
    age: 24
  })
}).then((response) => {
  alert('Success!');
}).catch((error) => {
  alert('Error!');
});
~~~


#### 【axiosの場合】

axiosの場合は、それぞれのHTTPメソッド毎にメソッドが用意されてます。

また、リクエストボディを設定する際は、第二引数に渡したいオブジェクトを設定します。
デフォルトでJSON形式にフォーマットされる為、`JSON.stringify`の記述は不要になります。

~~~javascript
axios.post('api/v1/users', {
  name: 'Whopper',
  age: 24
}).then((response) => {
  alert('Success!');
}).catch((error) => {
  alert('Error!');
});
~~~

---
### 3. JSONの取得
#### 【fetchの場合】
fetchでは、レスポンスにJSONデータを含む場合、基本的に`then`が2つあったりします。

1つめの`then`の中で実行されている`response.json()`というやつが、レスポンスのJSONを解析しているメソッドになります。

その返り値を、2つめの`then`の引数として渡されているような感じです。

~~~javascript
fetch('https://catfact.ninja/fact').then((response) => {
  return response.json();
}).then((responseJson) => {
  console.log(responseJson);
});
~~~

#### 【axiosの場合】
axiosでは、レスポンスオブジェクトの`data`へアクセスすることで、JSON解析済みのオブジェクトを参照することができます。

JSON解析のために`then`を増やす必要がないので、見た目もすっきりします。
~~~javascript
axios('https://catfact.ninja/fact').then((response) => {
  console.log(response.data);
});
~~~


---
### 4. インターセプター
#### 【fetchの場合】
fetchでは、標準機能を用いてインターセプターを実装することができません。

[fetch-intercept](https://www.npmjs.com/package/fetch-intercept)というライブラリを使用したり、fetchメソッド自体をオーバーライドしたりする必要があります。


#### 【axiosの場合】
axiosでは、標準機能でインターセプターを実装することができます。

以下では、リクエストを送信する際に`リクエスト送信`というログを出すリクエストインターセプターを定義しています。
~~~javascript
axios.interceptors.request.use((config) => {
  console.log('リクエスト送信');
  return config;
});
~~~

以下では、レスポンスを受信した際に`レスポンス受信`というログを出すレスポンスインターセプターを定義しています。
~~~javascript
axios.interceptors.response.use((response) => {
  console.log('レスポンス受信');
  return response;
});
~~~

---
### 5. リクエスト時の共通設定を定義
axiosでは`create`メソッドを利用することでaxiosのインスタンスが作成できます。
これを利用することで、リクエスト毎のタイムアウトの時間や、ベースURLや、共通のヘッダーなどを定義することができます。


~~~javascript
const baseRequest = axios.create({
  baseUrl: 'https://catfact.ninja',
  timeout: 2000
});

baseRequest.get('/fact').then((response) => {
  console.log(response.data);
}).catch((error) => {
  console.error(error);
});
~~~

---
### まとめ

まとめると、axiosは以下の点でfetchより使いやすいと考えています。

1. 200~299以外のステータスの場合は`catch`が実行される
2. HTTPメソッド毎にメソッドが用意されているのでGET以外の通信が楽
3. レスポンスオブジェクトの`data`プロパティで、解析済みのJSONデータを取得できる
4. インターセプターの実装が楽
5. 通信時の共通設定の定義が楽

他にも、「axiosはこういう点も便利!」や「fetchはこういう点で素晴らしい!」などがありましたら、是非コメントをお願いします!
最後まで読んでいただき、ありがとうございました!