Axiosのdeleteでデータを送りたい

今回、React側とAWS lambda上のそれぞれでAxiosのdeleteでデータを送る必要があったのですが、たまたま実装の仕方が違ったことが原因で分かったこととしったことを忘れないようにするために記事にしました。


#### そもそもAxiosってなに？


```
※以下Axiosの公式ドキュメントの見出しより
Axios is a simple promise based HTTP client for the browser and node.js. 
Axios provides a simple to use library in a small package with a very extensible interface.

Axiosは、ブラウザーとnode.js用のシンプルなPromiseベースのHTTPクライアントです。
Axiosは、非常に拡張可能なインターフェイスを備えた小さなパッケージで、使いやすいライブラリを提供します。
```
ドキュメントに書いてある通りですが、シンプルにHTTPリクエストを実行できる便利なライブラリになります。
sample

```react
import axios from "axios";
axios.get('/path')
  .then((res) => {
    console.log(res.data)
  });
```
このように非常に短い内容で済みXMLHttpRequestのように長々と記述する必要のない便利なライブラリです。

#### 何が起きたか
冒頭にも記載しましたがフロントでもともと呼び出す予定のAPIがIP制限の関係でlambda上で呼び出す必要が発生しました。
その際にリクエストの仕方が違ったためlambda側でうまくコールができないといった事象が起きました。

フロント側は一つのファイルにaxiosを呼び出すため処理が書いてありその関数をimportしてほかの箇所で使いまわすつくりになっていました。
axiosの関数(getなど)は使わずに第2引数のmethodにGETやPOSTを渡して使うのでdeleteの時も第3引数のbodyにあたる内容も送れていました。

```react
import axios from "axios";
export const invokeApi = (path, method, data,  options?) => {
  const headers = {
    'Content-Type': 'application/json'
  };
  return axios({
    url: `url/${path}`,
    method,
    data,
    headers: extend(headers, options)
  });
};
```
使いたい箇所

```react
import { invokeApi } from '/file';
invokeApi('path', 'DELETE', { ids: [1,2,3] });
```

lambda側では使いまわすといったことはないため、フロント側とは違ってAxiosの関数であるdeleteをドキュメントなど一切読まずにpostと同じように呼び出しました。
有識者の方はこの時点でお気づきだと思いますが、リクエストを送っているAPI側からは`error ids required`といったエラーが返ってきていました。

```javascript
const axios = require('axios');
exports.handler  = async(event, context, callback) => {
  const { headers, body, pathParameters, queryStringParameters } = event;
  const headers = {
    'Content-Type': 'application/json'
  }
  const res = await axios.delete(
    'url',
    body,
    { headers }
  );
}
```

#### 原因
根っこの原因はAxiosのHTTPリクエストメソッドごとに実装が分かれているのが原因となります。
[Axios github](https://github.com/axios/axios/blob/master/lib/core/Axios.js)より抜粋

```javascript
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

```
このようにGET, DELETEとPOST, PUT, PATCHで実装が異なっています。
おそらくですが、GET、DELETEは仕様上bodyをつけることは禁止されていないのですが、POST, PUTはcontent-lengthを付けることが必須なのに対して
GET, DELETEは任意で必須ではないためこのようになっているだと思われます。

なのでlambdda側の関数をこのように修正したところAPIからエラーが返ってくることがなく無事に動きました。

```diff
  const res = await axios.delete(
    'url',
-   body 
-   { headers }
+   {
+     headers,
+     data:body
+   }
  );

```

#### まとめ
たまたま発生した事態でいろいろと調べてHTTP リクエストを雰囲気で使っていることを痛感しました
そもそもbodyを用意するためにはcontent-lengthが必要なこともふんわりPOSTとPUTの時はあることは気が付いていましたが、GETとDELETEにはないのは今まで気が付いていなかったのでしっかり理解して使っていかないと大変なことになりそうです。