TypeScript バックエンドで Axios を使ってみた

TypeScript バックエンドで Axios を使ってみた

2026.07.01

製造ビジネステクノロジー部の小林です。

Axios は HTTP 通信をシンプルに扱える JavaScript / TypeScript 向けのライブラリです。Axios の理解をもっと深めたかったので、Node.js のバックエンド(Express)から外部 API を叩く構成で、実際に使っていろいろ検証してみました!

Axios とは?

HTTP 通信をシンプルに扱える JavaScript / TypeScript 向けのライブラリです。サーバーとのデータのやり取り(API 呼び出し)をシンプルに書けるように設計されています。

https://axios-http.com/

この記事では Node.js(バックエンド)から外部 API を呼び出すケースを前提に進めます。Lambda や Express サーバーから別のサービスの REST API を叩く、というサーバーサイドの構成です。

「とりあえずサーバーと通信できればよい」のであれば、Node.js 標準の fetch で十分事足ります。一方で Axios の強みは、実務で「あると助かる」機能が最初から組み込まれている点にあります。たとえば、

  • レスポンス JSON の自動パース
  • 4xx / 5xx の自動 throw(ステータスチェックを書き忘れる事故が起きない)
  • 共通処理を差し込めるインターセプター
  • リクエスト単位のタイムアウト指定
  • リトライやモックなど、周辺ライブラリのエコシステム

といったあたりが、追加実装なしですぐに使えます。fetch でも同じことは実現できますが、自前で書くと「ラッパー関数が育っていく」という状況になります。

この記事では、まず fetch と Axios の違いをコードで見比べたあと、後半で Express アプリに組み込んで実際に使用してみます。

Axios の GitHub リポジトリ: https://github.com/axios/axios

標準機能 fetch との違い

そもそも Node.js の fetch とは?

fetch は元々ブラウザに搭載されていた API ですが、Node.js でも v18 から標準で組み込まれ、v21 で stable になりました。node-fetch などのライブラリを別途入れる必要はなく、import なしでグローバルに使えます。

// import 不要
const response = await fetch("https://api.example.com/users/1");
const data = await response.json();

Axios も fetch も、やりたいこと(サーバー間の通信)は同じです。違いを次のようにまとめてみました。

機能 Axios fetch
JSON 自動パース(受け取ったデータを使える形に変換) できる 手動で .json() が必要
インターセプター(リクエスト/レスポンスの横取り) できる できない
タイムアウト設定 timeout オプションで可能 AbortSignal.timeout() などが必要
HTTP エラーの自動 throw 4xx/5xx で例外を投げる 自分でステータス確認が必要
リクエストのキャンセル できる AbortController で可能

Axios を使う場合 と 使わない場合

同じ処理を「fetch で書いた場合」と「Axios で書いた場合」で、Node.js のサーバー側コードとして並べてみます。

① 外部 API から JSON を取得するだけのリクエスト

fetch には実務で気をつけたい挙動が 2 つあります。レスポンスボディを取り出すには .json() を明示的に呼ぶ必要があること、そして 4xx / 5xx のステータスでも Promise が reject されないことです。response.ok を自前でチェックしないと、エラーレスポンスを正常系としてそのまま通してしまいます。

// fetch の場合(Node.js)
const response = await fetch("https://api.example.com/users/1");
if (!response.ok) {
  // ステータス確認は自前。忘れるとエラーレスポンスが成功扱いで通過する
  throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json(); // JSON パースも自前

一方 Axios は、JSON を自動でパースし、4xx / 5xx のレスポンスは自動的に例外として throw されます。呼び出し側は try/catch で受けるだけで済みます。

// Axios の場合
import axios from "axios";

const { data } = await axios.get("https://api.example.com/users/1");
// JSON は自動パース、4xx/5xx は自動 throw されるので try/catch で拾うだけ

② 全リクエストに共通処理を入れたい

バックエンドの開発を進めると、「外部 API への通信すべてに認証ヘッダーを付けたい」「通信のたびに CloudWatch にログを残したい」といった横断的な要件が出てきます。

fetch の場合:自前のラッパー関数を育てる

fetch 自体には、共通処理を差し込む仕組みがありません。そのため、共通処理を詰め込んだラッパー関数(fetch を包んだ自作関数)を用意し、通信のたびにそれを経由させる形になります。

// fetch の場合:こういうラッパー関数を自前で育てていくことになる
async function request(url: string, options: RequestInit = {}) {
  // 認証ヘッダーを付与
  const headers = { ...options.headers, Authorization: `Bearer ${token}` };
  logger.info("request", { url });

  const res = await fetch(url, { ...options, headers });

  // エラーチェックも自前
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  logger.info("response", { url, status: res.status });
  return res.json();
}

Axios の場合:インターセプターで一度だけ登録する

Axios にはインターセプターという仕組みがあります。リクエスト送信前・レスポンス受信後に共通処理を差し込めるフックで、一度登録しておけば、以降は普段どおり instance.get() を呼ぶだけで自動的に適用されます。

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

// Axios の場合:1 回登録すれば、以降すべてのリクエストに適用される
// リクエスト送信前:認証ヘッダーの付与とログ出力
instance.interceptors.request.use((config) => {
  config.headers.Authorization = `Bearer ${token}`;
  logger.info("request", { url: config.url });
  return config;
});

// レスポンス受信後:ログ出力(4xx/5xx は自動 throw されるので catch 側で拾える)
instance.interceptors.response.use((response) => {
  logger.info("response", {
    url: response.config.url,
    status: response.status,
  });
  return response;
});

呼び出し側のコードからは共通処理が完全に消え、各リクエストは「どの URL に何を送るか」だけに集中できます。

どちらを選ぶか

共通処理が増えるほど、Axios のインターセプターが効いてきます。逆に言えば、

  • 外部ライブラリへの依存をできるだけ減らしたい
  • 通信は単純な GET が数本だけで、共通処理もほぼ不要

といったケースでは、Node.js 標準の fetch で十分まかなえます。

https://nodejs.org/api/globals.html#fetch

代替ライブラリはなにがある?

ライブラリ 特徴 向いている場合
fetch JavaScript 標準。追加インストール不要 シンプルな通信のみの場合
Got Node.js 専用。軽量で高速 Node.js サーバーサイドのみ
jQuery jQuery 付属。昔からの定番 jQuery を既に使ってる場合
node-fetch fetch を Node.js で使える fetch の互換性が必要な場合
undici 低レベルの制御が可能 パフォーマンス重視
Axios 実務的な機能が豊富。モダン ほとんどのケースで使える

基本的な使い方

GET リクエスト

import axios from "axios";

const response = await axios.get("https://api.example.com/users/1");
console.log(response.data); // JSON が自動でパースされる

レスポンスは AxiosResponse 型でラップされており、data(レスポンスボディ)、statusheaders などにアクセスできます。

POST リクエスト

const response = await axios.post("https://api.example.com/users", {
  name: "Taro",
  email: "taro@example.com",
});

第 2 引数のオブジェクトは Axios が自動で JSON 文字列化し、Content-Type: application/json も自動で付与してくれます。JSON.stringify を書く必要はありません。

そのほかの設定項目(認証、プロキシ、リトライ、レスポンスの変換など)は公式ドキュメントに網羅されています。

やってみた

ここからは Express から外部 API を叩く小さな構成で、Axios の「あると嬉しい」ポイントを 4 つ、順番に試していきます。

axios.create で共通設定を 1 か所にまとめる

baseURL や共通ヘッダーを持ったインスタンスを 1 つ作っておくと、呼び出し側は毎回 URL 全体やヘッダーを書かずに済みます。

import axios from "axios";

// baseURL・共通ヘッダーをまとめたインスタンスを 1 つ作る
const client = axios.create({
  baseURL: "https://jsonplaceholder.typicode.com",
  headers: { "Content-Type": "application/json" },
});

// 以降は「パスだけ」で呼べる
const { data } = await client.get("/posts/1");

axios.create() に渡した設定は、このインスタンス経由の全リクエストに効きます。認証ヘッダーもここに 1 回書けば、各呼び出しから消えます。

② インターセプターで共通処理を「1 回だけ」仕込む

「全リクエストにログを残したい」「全リクエストに認証を付けたい」——こうした横断的な処理を、各呼び出しに書かずに 1 か所へ集約できるのがインターセプターです。

// リクエスト送信前:ログ出力(認証ヘッダーの付与などもここで)
client.interceptors.request.use((request) => {
  console.log("API Request", { url: request.url, method: request.method });
  return request; // 必ず return する
});

// レスポンス受信後:ログ出力(4xx/5xx は自動 throw されるので catch 側で拾える)
client.interceptors.response.use((response) => {
  console.log("API Response", { status: response.status });
  return response; // 必ず return する
});

一度登録すれば、あとは普段どおり client.get() を呼ぶだけ。呼び出し側のコードからログ処理が完全に消えます。ハンドラに console.log を 1 行も書かなくても、全リクエスト・レスポンスがログに残ります。

③ axios-retry でリトライを数行で足す

一時的な 5xx に備えたリトライも、axios-retry を使えばインスタンスに数行で足せます。

https://github.com/softonic/axios-retry

import axiosRetry from "axios-retry";

axiosRetry(client, {
  retries: 1, // 最大リトライ回数
  // 5xx のみリトライ(4xx はクライアント側起因なのでリトライしない)
  retryCondition: (error) =>
    error.response ? error.response.status >= 500 : false,
  // 指数バックオフ + フルジッター(リトライの集中を防ぐ)
  retryDelay: (retryCount) => Math.floor(Math.random() * 2 ** retryCount * 50),
});

retryDelay にランダム性を入れているのは、複数インスタンスが同時に 500 を受けたとき、リトライのタイミングが重なってサーバーを叩き続けるのを防ぐためです(考え方は AWS Builders' Library が分かりやすいです)。

④ 4xx/5xx は自動 throw → isAxiosError で確実に拾う

Axios は 4xx/5xx を自動で例外として throw するので、try/catch で必ず捕まえられます。fetch だと「response.ok を見て自分で throw する」処理を書き忘れると、エラーレスポンスが成功扱いで素通りしてしまいますが、Axios ではその取りこぼしが構造的に起きません。

import axios from "axios";

try {
  const { data } = await client.get(`/items/${id}`);
  return httpUtil.ok(data); // 成功
} catch (error) {
  // Axios が throw したエラーかを型ガードで判定
  if (axios.isAxiosError(error) && error.response) {
    // 上流の 5xx は 502、それ以外は 500 に寄せて返す
    return toErrorResponse(error.response.status);
  }
  throw error; // 想定外のエラーは再 throw
}

toErrorResponse は上流ステータスを見て「5xx→502 / それ以外 →500」を返すだけの小さなヘルパーです。エラー判定の isAxiosError は Axios 公式の型ガードで、err instanceof AxiosError よりも安全とされています。依存ツリー内に複数バージョンの Axios が混在しても判定をすり抜けません。

動かして確認する

ローカルで起動して curl で叩くと、ここまでに実装した 4 つの機能がそのまま効いていることを確認できます。

正常系:データの取得
存在する ID を指定して、アイテムを 1 件取得するリクエストです。

# 成功:JSON が自動パースされて返る
curl http://localhost:3000/items/1

上流 API から返ってきた JSON が、Axios によって自動でパースされ、そのままレスポンスとして返ってきます。id や title などを含むオブジェクトが取得できていることが確認できます。

スクリーンショット 2026-07-01 0.08.32

異常系:上流エラーのハンドリング
存在しない ID(0)を指定して、エラー時の挙動を確認するリクエストです。-i オプションでレスポンスヘッダーも合わせて表示しています。

# 失敗:上流が 404 → isAxiosError で捕捉 → 500 に変換して返す
curl -i http://localhost:3000/items/0

上流 API が 404 を返すと、それを isAxiosError で捕捉し、こちらの API としては 500 に変換して返しています。下のスクリーンショットのように、ステータスコード 500 とエラーレスポンスが返ってきていることが確認できます。
スクリーンショット 2026-07-01 0.09.38

POST:データの作成
新しいアイテムを作成するリクエストです。リクエストボディに JSON を渡しています。

# ③ POST:JSON.stringify 不要・Content-Type も自動付与
curl -X POST http://localhost:3000/items \
  -H "Content-Type: application/json" \
  -d '{"title":"Taro","body":"hello","userId":1}'

Axios 側では JSON.stringify を呼ぶ必要がなく、オブジェクトを渡すだけで自動的に JSON 文字列へ変換され、Content-Type: application/json も自動で付与されます。作成されたアイテムが返ってきていることが確認できます。

スクリーンショット 2026-07-01 0.10.28

ログ出力:インターセプターの動作確認
上記の各リクエストを実行している間、ターミナルには以下のようなログが出力されています。

ターミナルには、インターセプター経由で [INFO] API Request / [INFO] API Response が全リクエスト分そのまま出力されます。ハンドラ側にはログを 1 行も書いていないにもかかわらず、共通の処理としてログが記録されている点がポイントです。

スクリーンショット 2026-07-01 0.17.22

まとめ

Axios を使ってみて感じたのは、「定型的な処理を最初から肩代わりしてくれる」便利さです。
使い分けの目安としては、以下のように考えると良さそうです。

  • 外部 API をたくさん叩く → Axios
  • 単純な GET が数本だけ → fetch

このくらいの基準で選べば、迷わずに判断できるのではないかと思いました。

参考リンク

https://github.com/axios/axios#fetch-vs-axios
https://github.com/softonic/axios-retry
https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/
https://nodejs.org/api/globals.html#fetch

この記事をシェアする

関連記事