[axios] APIのリクエストにインターバルを設ける

## はじめに
APIのリクエスト処理を作成した際に、ステータスコードが429(Too Many Requests)で返ってきてヒヤっとした経験はないでしょうか？
サードパーティのAPIを利用する場合には、プロバイダがAPIのリクエスト頻度を制限していることが一般的です（例えば、1秒間に10回までなど）
上記の理由で、リクエスト頻度を意識する必要があります

Node.js には、AxiosというHTTPクライアントライブラリがあります

https://axios-http.com/

本記事では、Axiosにリクエスト間隔を設定する方法の一例を紹介したいと思います！

## ゴール
axiosを使って、APIのリクエスト間隔を制御できる

## 環境情報
| 項目       | バージョン | 
| :--------: | :--------- | 
| Node.js    | 20.11.1    | 
| TypeScript | 5.4.3      | 
| Axios      | 1.6.8      | 

## 方針
以下の方針で実装をしていきます
- axiosのインスタンス単位でリクエスト間隔を制御する
    - 同一インスタンスを使用して、リクエストする場合にはあらかじめ設定したリクエスト間隔を守ってリクエストを行う
    - 異なるインスタンスでは、それぞれ独立してリクエスト間隔を設定できる
- axiosのinterceptorsの機能を利用して、リクエストに対して待機処理を追加する
- axios.createの使用感を維持するため、引数の拡張で対応する

### interceptors
axiosには、interceptorsがあります
interceptorsを使用することで、リクエストやレスポンスの処理の前に特定の処理を差し込むことができます

https://axios-http.com/ja/docs/interceptors

今回は、このinterceptorsを使用して、APIリクエスト前にあらかじめ指定したミリ秒だけ待機するようにします

## 実装例
以下のフォルダ構成でサンプルを作成してみます

- `project-root/`
    - `api/`
        - `sampleApiClient.ts`
    - `utils/`
        - `axios.ts`
    - `index.ts`

### `utils/axios.ts`
`axios.create`を拡張し、リクエスト間隔を調整できる`axiosEx.create` を定義します

```typescript:utils/axios.ts
import axios from "axios";

const createAxiosInstance: CreateAxiosInstance = ({
  interval = 0, 
  ...param
} = {}) => {
  const instance = axios.create(param);

  // 間隔が必ず0ミリ秒以上になるように調整する
  const requestInterval = interval > 0 ? interval : 0;
  
  if (requestInterval > 0) {
    instance.interceptors.request.use(async (config) => {
      await sleep(requestInterval);
      return config;
    });
  }

  return instance;
};

const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));

// exportして他のファイルから呼び出せるようにする
export const axiosEx = {
  create: createAxiosInstance,
};

// 以下、型定義
type Axios = typeof axios;
type AxiosCreate = Axios["create"];
type AxiosCreateParam = Parameters<AxiosCreate>[0];
type AxiosCreateReturn = ReturnType<AxiosCreate>;

type AxiosExCreateParam = AxiosCreateParam & {
  /**
   * リクエスト間の待機時間 [ms]
   * @default 0
   */
  interval?: number;
};
type AxiosExCreateReturn = AxiosCreateReturn;
type CreateAxiosInstance = (params?: AxiosExCreateParam) => AxiosExCreateReturn;

```


### `api/sampleApiClient.ts`
`axiosEx.create` を呼び出して、axiosのインスタンスを用意します
リクエスト間隔を指定する`interval`には、500ミリ秒を指定しておきます

```typescript:api/sampleApiClient.ts
import {axiosEx} from "../utils/axios";

// exportして他のファイルから呼び出せるようにする
export const sampleApiClient = axiosEx.create({
  baseURL: "http://localhost:3000",
  interval: 500, // [ms]
});

```

### `index.ts`
`sampleApiClient` を呼び出します

```typescript:index.ts
import {sampleApiClient} from "./api/sampleApiClient";

const main = async () => {
  const names = [
    "Noby",
    "Sue",
    "Big G",
    "Sneech",
    "Doraemon",
  ];

  // 配列の処理が非同期で行われないように、呼び出し側で要注意
  for (const name of names) {
    // 500ms待ってから、APIリクエストを行う
    const { data } = await sampleApiClient.get<string>("/api/hello", {
      params: { name },
    }).catch((error) => {
      console.error(error);
      throw error;
    });
    console.log(data); // e.g. Hello, Noby!
  }
};

main();

```

## まとめ
今回は、axiosで簡易的にリクエスト間隔を調整する方法を紹介しました
（別の方法としてaxiosのインスタンスをステートフルにしてタイムスタンプを管理するようにしたら、より厳密にリクエスト間隔を調整できそうな気がします）

理想を言えば、axiosが公式にAPIのリクエストの間隔を設定できるようなアップデートがあると嬉しいです

↓ 今回試した内容はGithubに公開しています。よければ参考にしてください

https://github.com/pa-y-kunimoto/axios-create-with-interval

## 参考
- https://axios-http.com/ja/docs/interceptors