Proxy環境下のNode.js環境からAxiosでkintoneにリクエストする方法について

概要は下記参照のこと。
https://qiita.com/sy250f/items/1e9f5e71a6aac5756f5c

企業内にはインターネット環境に出ていく際に、Proxyサーバ経由の場合がある。
Proxyが間にいると開発する際にいろいろ問題が出てくるが、Node環境からkintoneへのリクエストに[kintone rest-api-client](https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client#readme)を利用した際に普通にパラメーターにProxyサーバの設定を入れただけでは動作しないことがあったので、備忘録として記載する。

# 問題点

kintone rest-api-clientを利用したプログラムから、企業Proxyを通してkintoneサーバにリクエストすると以下のエラーが発生した。

```
400: Bad Request
```

# 問題点の切り分け

問題の切り分けのために、下記の通り3つのケースにて簡単なプログラムを実行して結果を確認した。

1. curlにてProxyサーバ設定をしてリクエスト実施
2. kintone rest-api-clientにProxyサーバ設定をしてリクエスト実施
3. axiosにてProxyサーバ設定をしてリクエスト実施

## 結果

1. curlにてProxyサーバ設定をしてリクエスト実施　→ OK
2. kintone rest-api-clientにProxyサーバ設定をしてリクエスト実施　→ NG
3. AxiosにてProxyサーバ設定をしてリクエスト実施　→ NG

上記の結果より、Axiosが原因なことが判明。

さらに詳細の原因を調査すると下記の情報を発見。

https://github.com/kintone/js-sdk-ja/issues/24

つまり、Axiosの仕様によりプロキシサーバからkintoneサーバへリクエストする際に、kintoneサーバが受け取ることができないリクエストになっていることで、400番のエラーが発生している模様。

# 対応方法

前段のissueにもあるとおり、AxiosはHTTPエージェントを切り替えることができるため、ここに別のHTTPエージェントを設定するようにする。

## Axios

直接Axiosを利用した場合。

HTTPエージェントには`https-proxy-agent`を利用。

```js
const axios = require('axios');
require('dotenv').config({debug: true});
const { HttpsProxyAgent } = require('https-proxy-agent');

(async () => {
    try {
        const API_TOKEN = process.env.API_TOKEN;
        const APP_ID = process.env.APP_ID;
        const agent = new HttpsProxyAgent(`http:${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`);
        const config = {
            headers: {
                'X-Cybozu-API-Token': API_TOKEN,
            },
            params: {
                'app': APP_ID,
                'id': 1,
            },
            httpsAgent: agent,
        }
        await axios.get(`https://${process.env.KINTONE_SUBDOMAIN}.cybozu.com/k/v1/record.json`, config)
        .then(response => {
            console.log(response);
        })
        .catch(e => {
            throw e;
        })
    } catch (error) {
        console.log(error);
    }
})();
```

HTTPエージェントに`hpagent`を利用した場合は下記の通り。

```js
const axios = require('axios');
require('dotenv').config({debug: true});
const { HttpsProxyAgent } = require('hpagent');

(async () => {
    try {
        const API_TOKEN = process.env.API_TOKEN;
        const APP_ID = process.env.APP_ID;
        const agent = new HttpsProxyAgent({
            proxy: `http:${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`,
        });
        const config = {
            headers: {
                'X-Cybozu-API-Token': API_TOKEN,
            },
            params: {
                'app': APP_ID,
                'id': 1,
            },
            httpsAgent: agent,
        }
        await axios.get(`https://${process.env.KINTONE_SUBDOMAIN}.cybozu.com/k/v1/record.json`, config)
        .then(response => {
            console.log(response);
        })
        .catch(e => {
            throw e;
        })
    } catch (error) {
        console.log(error);
    }
})();
```


## kintone rest-api-client

rest-api-clientではコンストラクタの`httpsAgent`パラメータに、HTTPエージェントを設定する。


```js
const { KintoneRestAPIClient } = require("@kintone/rest-api-client");
require('dotenv').config({debug: true});
const { HttpsProxyAgent } = require('https-proxy-agent');

(async () => {
    try {
        const API_TOKEN = process.env.API_TOKEN;
        const APP_ID = process.env.APP_ID;
        const params = {
            'app': APP_ID,
            'id': 1,
        }
        const agent = new HttpsProxyAgent(`http:${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`);
        const client = new KintoneRestAPIClient({
            baseUrl: `https://${process.env.KINTONE_SUBDOMAIN}.cybozu.com`,
            auth: { apiToken: API_TOKEN },
            httpsAgent: agent,
        });
        await client.record.getRecord(params)
        .then(response => {
            console.log(response);
        })
        .catch(e => {
            throw e;
        })
    } catch (error) {
        console.log(error);
    }
})();
```

# 後書き

上記はあくまで一つの例なので、企業プロキシごとに対応方法は異なるかと思う。
ケースにより、HTTPエージェントは切り替える必要がある。
通信の状況によりHTTPエージェントの設定も、デフォルトではなくて細かな設定が必要な場面もあるので、都度対応が必要。