White Box技術部

WEB開発のあれこれ(と何か)

【TypeScript】nodeのfetchとaxiosのタイムアウト処理

戒めを込めて。

最初からaxiosを使おう(結論)

HTTPクライアントとしてFetch APIが標準化されてから、「標準とか言うならfetch使うべき?」とか思ってリクエストをfetchで実装し、 しばらくしてタイムアウト処理が欲しくなった段階で、「これなら最初からaxios使っておけばよかった」となる理由をコード比較で見ていきたいと思います。

fetchでのタイムアウト処理の書き方

fetchはnode18くらいから、外部ライブラリなしで利用することができます。 ですが、リクエスタイムアウトをオプション等で実現することはできないため、以下のようなコードを書く必要があります。

タイムアウト設定

const fetchWithTimeout = async (
  url: string,
  method: string,
  body?: string,
  timeout = 10000
): Promise<Response> => {
  const controller = new AbortController();
  const timer = setTimeout(() => {
    console.error(`${method} ${url} timeout!`);
    controller.abort();
  }, timeout);

  const options: RequestInit = {
    method,
    body,
    headers: body
      ? {
          Accept: "application/json",
          "Content-Type": "application/json",
          "Content-Length": `${body.length}`, // ここまでしなくても動きはする
        }
      : {
          Accept: "application/json",
        },
    signal: controller.signal,
  };
  const response = await fetch(url, options);
  clearTimeout(timer);

  return response;
};

コード量に「え・・・」ってなりますね・・・

使い方

fetchWithTimeoutは、fetchをほぼラップしただけなので、使う方は難しくはありません。
async/awaitで書くとこんな感じです。

type SampleResponse = { result: string };

export const sampleProcess = async (url: string): Promise<void> => {
  // GET
  const response = await fetchWithTimeout(url, "GET");
  if (response.ok) {
    const data: SampleResponse = await response.json();;
    console.log(data);
  } else {
    // エラー処理
    const data = await response.json(); // もし500エラー等でJSONを戻してなければ、この書き方はエラーになる
    console.error(data);
  }

  // POST
  const body = JSON.stringify({ id: "sample_id", name: "sample" });
  const response = await fetchWithTimeout(url, "POST", body);
  if (response.ok) {
    const data: SampleResponse = await response.json();
    console.log(data);
  } else {
    // エラー処理
  }
};

axiosでのタイムアウト処理の書き方

一方、axiosを使ってタイムアウト付きリクエストを行う手順です。

インストール

axiosは型定義もライブラリに入っているので、TypeScriptで使う場合もaxiosだけを追加すればOKです。

$ npm i -E axios
or
$ yarn add -E axios

最近、Eオプションを付けてインストールするようになりました。こうするとバージョン番号に^が付かなくなります。

タイムアウト設定と使い方

import axios from "axios";

// 設定部分
const client = axios.create({
  timeout: 10000,
  headers: {
    Accept: "application/json",
  },
});

// 使用例
type SampleResponse = { result: string };

export const sampleProcess = async (url: string): Promise<void> => {
  try {
    // GET
    const result = await client.get<SampleResponse>(url);
    console.log(result.data);

    // POST
    const body = JSON.stringify({ id: "sample_id", name: "sample" });
    const result = await client.post<SampleResponse>(url, body);
    console.log(result.data);

  } catch (error) {
    console.error(error);
  }
};

axiosだとfetchWithTimeoutの定義部分と同じくらいのコード量で使用例まで書けますね。

axios自体をもっと知りたい場合は公式を参照してください。

ということで、「こんなことなら最初からaxios入れておけばよかった・・・」とタイムアウト処理を書くときに思った理由のコード比較でした。