- 技術 💻
axiosの使い方|GET・POST・エラー処理・インターセプターを実例で解説
結論
axiosは、ブラウザとNode.jsからHTTPリクエストを送るためのJavaScriptライブラリです。読み方は「アクシオス」が一般的です。
この記事でわかること
- 状況別10パターンの書き方の早見表
- 擬似APIが受けたリクエストとエラー10種の原文
- 401で再試行するインターセプターの完成コード
axios.get() などで送ったリクエストは、インターセプター(前後に差し込む共通処理)を通ってサーバーへ届き、2xx の応答なら res.data、それ以外や通信の失敗なら catch に入ります。
flowchart TD
A[axios.get など] --> B[リクエストの<br>インターセプター]
B --> C[(API サーバー)]
C --> D[レスポンスの<br>インターセプター]
D -->|2xx| E[then で res.data]
D -->|それ以外・通信失敗| F[catch で error]
よく使う書き方を先にまとめます。
| 状況 | 書き方 | 備考 |
|---|---|---|
| データを取る | axios.get(url, { params }) |
JSON は res.data に入る |
| 登録する | axios.post(url, data) |
オブジェクトは JSON で送られる |
| 更新する | axios.put / axios.patch |
全体の置き換え/一部の変更 |
| 削除する | axios.delete(url) |
204 なら res.data は空 |
| ヘッダーを付ける | { headers: { Authorization } } |
第2引数(POST は第3引数) |
| 共通の設定 | axios.create({ baseURL, timeout }) |
以降は相対パスで呼べる |
| トークンを毎回付ける | interceptors.request.use |
401 の再試行は response 側 |
| エラーの中身を見る | error.response?.status |
無ければ応答が届いていない |
| 途中で止める | { signal: controller.signal } |
ERR_CANCELED になる |
| 時間で切る | { timeout: 5000 } |
既定は 0(無制限) |
確認環境:Ubuntu 24.04・Node.js 22.22.2・npm 10.9.7・axios 1.20.0(npm の latest、2026年10月22日時点)。Node.js で自作の擬似 API(127.0.0.1:30900)を立て、この記事のコードを全部実行して、擬似 API が受け取ったリクエスト(メソッド・URL・ヘッダー・本文)と、axios の戻り値・エラーを記録しました。TypeScript 7.0.2 で型検査し、React 19.3.1 は jsdom 30.1.3 の中で StrictMode で動かしました。axios-retry 4.5.0 と ky 2.2.0 は npm で確かめました。仕様は axios の README(1.20.0 同梱)・axios-http.com・GitHub のリリースで確かめました(確認日 2026年10月22日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「axiosの使い方|GET・POST・エラー処理・インターセプターを実例で解説」
axiosとは(読み方・できること)
この節では、axios が何をするライブラリで、どこで動くかが分かります。
axios は、Promise を返す HTTP クライアントです。 同じ書き方でブラウザでも Node.js でも動きます(公式は「isomorphic」と呼んでいます)。読み方は日本語では「アクシオス」がよく使われます。ライセンスは MIT です。
中では、環境ごとに通信の部品(アダプター)を切り替えています。既定の順番は ['xhr', 'http', 'fetch'] で、使えるものが選ばれます。
| 動く場所 | 使われるアダプター | 中身 |
|---|---|---|
| ブラウザ | xhr |
XMLHttpRequest |
| Node.js | http |
Node.js の http・https モジュール |
| どちらも無い環境 | fetch |
fetch API(1.7.0 から) |
1JSON の変換が自動
送るオブジェクトは JSON にされ、受けた JSON は res.data にオブジェクトで入る。
24xx・5xx で例外になる
2xx 以外は catch に入る。if (!res.ok) を毎回書かなくてよい。
3インターセプター
トークンの付与や 401 の処理を、全部のリクエストに1か所で差し込める。
4timeout とキャンセル
timeout の1行で時間を切れる。AbortController にも対応。
標準の fetch との違いは、後の「axiosとfetchの違い」の節で、同じ処理を書き比べた実物で示します。
axiosのインストールと読み込み(npm・CDN)
この節では、axios を入れて読み込むまでと、入っている版の確かめ方が分かります。
パッケージマネージャーごとのコマンドです(README のとおり)。
npm install axios
yarn add axios
pnpm add axios
bun add axios
読み込みは、ES モジュールなら import、CommonJS なら require です。
// ES モジュール("type": "module" や .mjs、バンドラー)
import axios from 'axios';
import { isAxiosError, isCancel } from 'axios'; // 名前付きでも取れる
// CommonJS(require では default の axios だけが取れる)
const axios = require('axios');
Node.js 22 で、import('axios') から取れる名前を全部表示しました。
Axios AxiosError AxiosHeaders Cancel CancelToken CanceledError HttpStatusCode VERSION all create default formToJSON getAdapter isAxiosError isCancel mergeConfig spread toFormData
ビルドの仕組みが無い HTML なら、CDN から読み込めます。版を固定して書きます。
<script src="https://cdn.jsdelivr.net/npm/axios@1.20.0/dist/axios.min.js"></script>
<script>
axios.get('/api/users').then((res) => console.log(res.data));
</script>
axiosの版を確かめる
| 目的 | コマンド | 出力(この記事の環境) |
|---|---|---|
| npm の最新版 | npm view axios version |
1.20.0 |
| 入っている版 | npm ls axios |
`-- axios@1.20.0 |
| 誰が入れたか | npm why axios |
axios@"^1.20.0" from the root project |
| 新しい版があるか | npm outdated axios |
最新なら何も出ない |
| 実行中の版 | axios.VERSION |
1.20.0 |
ほかのパッケージが間接的に入れている axios の版をそろえたいときは、npm の overrides(package.json)で指定できます。
{
"overrides": {
"axios": "1.20.0"
}
}
axiosの基本の使い方:GET・POST・PUT・PATCH・DELETE
この節では、5つのメソッドの書き方と、サーバーに実際に届いた中身が分かります。
メソッドごとの別名(エイリアス)の引数は次のとおりです。本文を送るメソッドだけ、第2引数が本文、第3引数が設定になります。
| メソッド | 書き方 | 本文 |
|---|---|---|
| GET | axios.get(url[, config]) |
なし |
| POST | axios.post(url[, data[, config]]) |
あり |
| PUT | axios.put(url[, data[, config]]) |
あり |
| PATCH | axios.patch(url[, data[, config]]) |
あり |
| DELETE | axios.delete(url[, config]) |
なし(送るときは config.data) |
| まとめて書く | axios({ method, url, data }) |
― |
擬似 API に向けて、5つを順に送りました。
import axios from 'axios';
const base = 'http://127.0.0.1:30900/api';
// GET
const res = await axios.get(`${base}/users`, { params: { page: 2, q: '佐藤' } });
console.log('GET ', res.status, res.statusText, JSON.stringify(res.data));
// POST(オブジェクトを渡すと JSON になる)
const created = await axios.post(`${base}/users`, { name: '高橋', role: 'dev' });
console.log('POST ', created.status, JSON.stringify(created.data), 'location=', created.headers.location);
// PUT(全体を置き換え)
const put = await axios.put(`${base}/users/3`, { name: '高橋', role: 'lead' });
console.log('PUT ', put.status, JSON.stringify(put.data));
// PATCH(一部だけ変更)
const patch = await axios.patch(`${base}/users/3`, { role: 'pm' });
console.log('PATCH', patch.status, JSON.stringify(patch.data));
// DELETE
const del = await axios.delete(`${base}/users/3`);
console.log('DELETE', del.status, JSON.stringify(del.data));
実行した結果です。
GET 200 OK {"query":{"page":"2","q":"佐藤"},"items":[{"id":1,"name":"佐藤"},{"id":2,"name":"鈴木"}]}
POST 201 {"id":3,"name":"高橋","role":"dev"} location= /api/users/3
PUT 200 {"id":3,"name":"高橋","role":"lead"}
PATCH 200 {"id":3,"name":"高橋","role":"pm"}
DELETE 204 ""
擬似 API に届いたリクエストです(host の行は省きました)。
--> GET /api/users?page=2&q=%E4%BD%90%E8%97%A4
user-agent: axios/1.20.0
accept: application/json, text/plain, */*
accept-encoding: gzip, compress, deflate, br
--> POST /api/users
user-agent: axios/1.20.0
accept: application/json, text/plain, */*
accept-encoding: gzip, compress, deflate, br
content-type: application/json
content-length: 30
body: {"name":"高橋","role":"dev"}
--> PUT /api/users/3
content-type: application/json
content-length: 31
body: {"name":"高橋","role":"lead"}
--> PATCH /api/users/3
content-type: application/json
content-length: 13
body: {"role":"pm"}
--> DELETE /api/users/3
読み取れることは次の3つです。
paramsのq: '佐藤'は、URL に%E4%BD%90%E8%97%A4とエンコードされて付いた。- POST・PUT・PATCH は
content-type: application/jsonが自動で付き、content-lengthは UTF-8 のバイト数(「高橋」は6バイト)だった。 - Node.js から送ると
user-agent: axios/1.20.0が付く。acceptも axios が付けている。
then と catch でも書けます。await と同じ動きです。
axios
.get('http://127.0.0.1:30900/api/users/1')
.then((res) => console.log(res.data))
.catch((error) => console.error(error.message))
.finally(() => console.log('終わり'));
レスポンスの中身(res.data・status・headers)
res のキーは status, statusText, headers, config, request, data の6つでした。
| プロパティ | 中身 | 上の GET での値 |
|---|---|---|
data |
応答の本文(JSON ならオブジェクト) | { query, items } |
status |
HTTP ステータスコード | 200 |
statusText |
ステータスの文言 | OK |
headers |
応答ヘッダー(名前は小文字) | res.headers['content-type'] は application/json; charset=utf-8 |
config |
このリクエストに使った設定 | baseURL・timeout など |
request |
Node.js は ClientRequest、ブラウザは XMLHttpRequest | ― |
204 No Content の応答では、res.data は空文字 "" でした。
paramsとheadersの渡し方(クエリ文字列の実物)
この節では、params に渡した値が、実際にどんな URL になるかが分かります。
クエリ文字列は URL に手で書かず、params にオブジェクトで渡します。 エンコードを axios がします。axios.getUri() で、送る前の URL を表示しました。
const api = axios.create({ baseURL: 'http://127.0.0.1:30900/api' });
console.log(api.getUri({ url: '/users', params: { ids: [1, 2] } }));
| params に渡したもの | できた URL のクエリ |
|---|---|
{ ids: [1, 2] } |
?ids%5B%5D=1&ids%5B%5D=2(ids[]=1&ids[]=2) |
同じ+paramsSerializer: { indexes: null } |
?ids=1&ids=2 |
同じ+paramsSerializer: { indexes: true } |
?ids%5B0%5D=1&ids%5B1%5D=2(ids[0]=1) |
{ a: null, b: undefined, c: '' } |
?c=(null と undefined は消える) |
{ q: 'a b&c=d', t: '10:00' } |
?q=a+b%26c%3Dd&t=10:00 |
{ from: new Date('2026-10-22T00:00:00Z') } |
?from=2026-10-22T00:00:00.000Z |
{ filter: { status: 'open' } } |
?filter%5Bstatus%5D=open |
new URLSearchParams([['tag','x'],['tag','y']]) |
?tag=x&tag=y |
配列は既定で ids[]=1 の形になります。 サーバー側が ids=1&ids=2 の形を待っている場合は、indexes: null を指定します。空白は + になり、: はそのまま残りました。厳密に %20・%3A にしたいときは、README のとおり paramsSerializer: { encode: encodeURIComponent } を使います。
ヘッダーは headers に渡します。GET では第2引数、POST では第3引数です。
await axios.get(`${base}/users/1`, {
headers: { Authorization: 'Bearer abc123', 'X-Request-Id': 'req-001' },
});
await axios.post(`${base}/users`, { name: '高橋' }, {
headers: { 'X-Request-Id': 'req-002' },
});
ユーザー名とパスワードの Basic 認証だけは auth: { username, password } で指定でき、axios が Authorization ヘッダーを作ります。Bearer トークンや API キーは headers に書きます(公式ドキュメント「Authentication」)。
GET で届いたヘッダーです。
--> GET /api/users/1
user-agent: axios/1.20.0
accept: application/json, text/plain, */*
accept-encoding: gzip, compress, deflate, br
authorization: Bearer abc123
x-request-id: req-001
送るデータの形式(JSON・フォーム・FormData・ファイル)
この節では、data に渡すものによって Content-Type と本文がどう変わるかが分かります。
擬似 API の /api/echo は、届いた Content-Type と本文をそのまま返します。
| data に渡したもの | 届いた Content-Type | 届いた本文 |
|---|---|---|
{ name: '佐藤', age: 30 } |
application/json |
{"name":"佐藤","age":30} |
new URLSearchParams({ name: '佐藤', age: '30' }) |
application/x-www-form-urlencoded;charset=utf-8 |
name=%E4%BD%90%E8%97%A4&age=30 |
axios.postForm(url, { name: 'sato', age: 30 }) |
multipart/form-data; boundary=… |
項目ごとのパート |
文字列 'plain text' |
application/x-www-form-urlencoded |
plain text |
| オブジェクト+ヘッダーで form を指定 | application/x-www-form-urlencoded |
name=sato&tags%5B%5D=a&tags%5B%5D=b |
文字列をそのまま渡すと、Content-Type は application/x-www-form-urlencoded になりました。 JSON の文字列を自分で JSON.stringify して渡すと、サーバーは JSON として読みません。オブジェクトのまま渡します。
postForm で届いた本文の実物です。
----------------------------11fe6d1503d8bc01e1a337ac
Content-Disposition: form-data; name="name"
sato
----------------------------11fe6d1503d8bc01e1a337ac
Content-Disposition: form-data; name="age"
30
----------------------------11fe6d1503d8bc01e1a337ac--
ファイルは FormData に入れて送ります。Content-Type は自分で付けません(README も、境界の文字列=boundary を付けるのは実行環境に任せるよう書いています)。Node.js 22 の FormData と Blob で送り、アップロードの進み具合も取りました。
import { readFile } from 'node:fs/promises';
const fd = new FormData();
fd.append('title', 'report');
fd.append('file', new Blob([await readFile('package.json')], { type: 'application/json' }), 'package.json');
let last;
await axios.post('http://127.0.0.1:30900/api/echo', fd, {
onUploadProgress: (e) => { last = e; }, // ブラウザでは進捗バーに使う
});
console.log(JSON.stringify({ loaded: last.loaded, total: last.total, progress: last.progress }));
upload content-type = multipart/form-data; boundary=…
onUploadProgress last = {"loaded":583,"total":583,"progress":1}
ブラウザでは <input type="file"> の files[0] をそのまま fd.append('file', file) に渡します。
axios.createでbaseURL・timeoutをまとめる(インスタンス)
この節では、共通の設定をまとめる方法と、設定がどの順で効くかが分かります。
API ごとに axios.create() でインスタンスを作り、そのインスタンスを使い回します。 毎回 URL の先頭やタイムアウトを書かずに済み、インターセプターもインスタンスごとに付けられます。
// api.js
import axios from 'axios';
export const api = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000, // 10秒。既定は 0(無制限)
headers: { 'X-Client': 'shop-web' },
});
// 使う側
const { data } = await api.get('/users', { params: { page: 1 } });
baseURL と url のつながり方を axios.getUri() で確かめました。
baseURL=http://127.0.0.1:30900/api url=/users → http://127.0.0.1:30900/api/users
baseURL=http://127.0.0.1:30900/api/ url=users → http://127.0.0.1:30900/api/users
baseURL=http://127.0.0.1:30900/api/ url=/users → http://127.0.0.1:30900/api/users
baseURL=http://127.0.0.1:30900/api url=https://example.com/x → https://example.com/x
スラッシュの重なりは axios が1つにします。url に https:// から始まる URL を渡すと、baseURL は使われません(allowAbsoluteUrls の既定が true のため)。README は、baseURL は安全のための境界ではないので、利用者の入力を url に使うときは先に検査するよう書いています。
設定は次の順に重ねられ、後のものが勝ちます。
flowchart TD
A[ライブラリの既定 timeout 0] --> B[インスタンスの defaults timeout 2500]
B --> C[リクエストの config timeout 5000 が使われる]
実際に axios.defaults.timeout は 0、axios.create({ timeout: 2500 }) で作ったインスタンスの defaults.timeout は 2500 でした。README によると、data(本文)は既定から引き継がれず、リクエストごとの指定だけが使われます。
axios.defaults.headers.common['Authorization'] = token のように、axios 本体の既定にトークンを入れる書き方もあります。README は、この方法では複数のドメインに送るとすべてにトークンが付くと注意しています。トークンは、そのAPI用のインスタンスに付けます。
インターセプター:トークン付与と401の再試行
この節では、全部のリクエストにトークンを付ける方法と、401 のときにトークンを更新して送り直す完成コードが分かります。
インターセプターは、リクエストの直前とレスポンスの直後に必ず通る関数です。 api.interceptors.request.use(関数) と api.interceptors.response.use(成功時, 失敗時) で登録します。
まず実行の順番です。2つずつ登録して動かしました。
inst.interceptors.request.use(tag('request 1'));
inst.interceptors.request.use(tag('request 2'));
inst.interceptors.response.use(tag('response 1'));
inst.interceptors.response.use(tag('response 2'));
await inst.get('/users/1');
request 2
request 1
response 1
response 2
リクエスト側は後から登録したものが先に、レスポンス側は登録した順に動きました。 README の説明(リクエストは LIFO、レスポンスは FIFO)のとおりです。
401のときにトークンを更新して1回だけ送り直す
アクセストークンの期限が切れて 401 が返ったら、更新用の API で新しいトークンを取り、元のリクエストを送り直す形です。流れは次のとおりです。
リクエストにトークンを付ける
リクエストのインターセプターで config.headers.Authorization に今のトークンを入れます。
401 を受けたら印を付ける
レスポンスのインターセプターの失敗時の関数で error.response.status === 401 を見て、config._retry = true にします。印があれば2回目なので送り直しません。
トークンの更新は1回にまとめる
同時に3件が 401 になっても、更新の API は1回だけ呼びます。更新中の Promise を変数に持ち、全員でそれを待ちます。
同じ設定で送り直す
return api(error.config) で、元のリクエストを新しいトークンで送ります。呼び出し元には、送り直した結果が返ります。
完成コードです。
import axios from 'axios';
// トークンの保管場所(例。実際は保存先をアプリに合わせる)
let accessToken = 'token-v1-expired';
const api = axios.create({
baseURL: 'http://127.0.0.1:30900',
timeout: 5000,
headers: { 'X-Client': 'shop-web' },
});
// リクエスト:毎回トークンを付ける
api.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${accessToken}`;
console.log(`[req] ${config.method.toUpperCase()} ${config.url} Authorization=${config.headers.Authorization}`);
return config;
});
// レスポンス:401 なら1回だけトークンを更新して再送する
let refreshing = null; // 同時に401が来ても更新は1回にまとめる
api.interceptors.response.use(
(res) => {
console.log(`[res] ${res.status} ${res.config.url}`);
return res;
},
async (error) => {
const original = error.config;
if (error.response?.status === 401 && !original._retry) {
original._retry = true;
console.log(`[res] 401 ${original.url} → トークンを更新して再送`);
refreshing ??= axios
.post('http://127.0.0.1:30900/auth/refresh') // 更新は api ではなく素の axios で(インターセプターの無限ループを防ぐ)
.then((r) => { accessToken = r.data.accessToken; })
.finally(() => { refreshing = null; });
await refreshing;
return api(original); // 同じ設定でもう一度
}
return Promise.reject(error);
}
);
// 3件同時に呼ぶ(全部 401 → 更新は1回 → 3件とも再送)
const results = await Promise.all([api.get('/api/me'), api.get('/api/me'), api.get('/api/me')]);
console.log('結果:', results.map((r) => r.status).join(', '), JSON.stringify(results[0].data));
擬似 API は、Bearer token-v2 以外には 401 を返し、/auth/refresh で token-v2 を渡します。実行した結果です。
[req] GET /api/me Authorization=Bearer token-v1-expired
[req] GET /api/me Authorization=Bearer token-v1-expired
[req] GET /api/me Authorization=Bearer token-v1-expired
[res] 401 /api/me → トークンを更新して再送
[res] 401 /api/me → トークンを更新して再送
[res] 401 /api/me → トークンを更新して再送
[req] GET /api/me Authorization=Bearer token-v2
[req] GET /api/me Authorization=Bearer token-v2
[req] GET /api/me Authorization=Bearer token-v2
[res] 200 /api/me
[res] 200 /api/me
[res] 200 /api/me
結果: 200, 200, 200 {"id":1,"name":"佐藤"}
擬似 API 側で見ると、更新の POST /auth/refresh は1回だけでした。
--> GET /api/me
authorization: Bearer token-v1-expired
--> GET /api/me
authorization: Bearer token-v1-expired
--> GET /api/me
authorization: Bearer token-v1-expired
--> POST /auth/refresh
--> GET /api/me
authorization: Bearer token-v2
--> GET /api/me
authorization: Bearer token-v2
--> GET /api/me
authorization: Bearer token-v2
更新の API そのものが 401 を返したとき(更新用のトークンも切れたとき)は、refreshing が失敗し、元の3件も失敗として catch に入ります。そこでログイン画面へ移す処理を書きます。401 と 403 のどちらを返すべきかは、関連記事「401と403の違い|HTTPステータスコードの使い分け判断フロー」で扱っています。
インターセプターを外すときは eject、全部外すときは clear です。
const id = api.interceptors.request.use(fn);
api.interceptors.request.eject(id); // 1つ外す
api.interceptors.response.clear(); // response 側を全部外す
axiosのエラー処理(error.responseとerror.requestの違い)
この節では、catch に入った error から「何が起きたか」を見分ける方法と、エラーごとの実物が分かります。
error は3段階で見分けます。 error.response があればサーバーは応答した(4xx・5xx)、response が無く error.request があれば送ったが応答が無い(接続できない・タイムアウト)、どちらも無ければ送る前に失敗した(設定や URL の誤り)です。
404 や 500 は前者。ECONNREFUSED・timeout・Network Error は後者。直す場所が、API 側か通信路・設定側かで分かれる
README の形をもとに、見分ける関数を作りました。
import axios from 'axios';
async function tryIt(label, fn) {
try {
const r = await fn();
console.log(`■ ${label}: 成功 status=${r.status} data=${JSON.stringify(r.data)}`);
} catch (e) {
console.log(`■ ${label}`);
console.log(` name=${e.name} code=${e.code} status=${e.status}`);
console.log(` message=${e.message}`);
console.log(` isAxiosError=${axios.isAxiosError(e)} response=${e.response ? 'あり' : 'なし'} request=${e.request ? 'あり' : 'なし'} isCancel=${axios.isCancel(e)}`);
if (e.response) console.log(` response.data=${JSON.stringify(e.response.data)}`);
}
}
const B = 'http://127.0.0.1:30900/api';
await tryIt('404 Not Found', () => axios.get(`${B}/users/999`));
await tryIt('500 Internal Server Error', () => axios.get(`${B}/error500`));
await tryIt('接続できない(サーバーが止まっている)', () => axios.get('http://127.0.0.1:30999/api/users'));
await tryIt('タイムアウト(timeout: 1000)', () => axios.get(`${B}/slow?ms=3000`, { timeout: 1000 }));
await tryIt('URL の書き間違い', () => axios.get('http:127.0.0.1:30900/api'));
実行した結果です(ほかの場合も同じ関数で試しました)。
■ 404 Not Found
name=AxiosError code=ERR_BAD_REQUEST status=404
message=Request failed with status code 404
isAxiosError=true response=あり request=あり isCancel=false
response.data={"error":"not_found","message":"user 999 not found"}
■ 500 Internal Server Error
name=AxiosError code=ERR_BAD_RESPONSE status=500
message=Request failed with status code 500
isAxiosError=true response=あり request=あり isCancel=false
response.data={"error":"internal","message":"DB connection failed"}
■ 接続できない(サーバーが止まっている)
name=Error code=ECONNREFUSED status=undefined
message=connect ECONNREFUSED 127.0.0.1:30999
isAxiosError=true response=なし request=あり isCancel=false
■ タイムアウト(timeout: 1000)
name=AxiosError code=ECONNABORTED status=undefined
message=timeout of 1000ms exceeded
isAxiosError=true response=なし request=あり isCancel=false
■ URL の書き間違い
name=AxiosError code=ERR_INVALID_URL status=undefined
message=Invalid URL "http:127.0.0.1:30900/api": missing "//" after protocol
isAxiosError=true response=なし request=なし isCancel=false
10の場合をまとめた表です。どれも Node.js 22 で実際に出た値です(CORS の行は、後の React の節の jsdom で出た値)。
| 起きたこと | error.code | message(原文) | response |
|---|---|---|---|
| 404 などの 4xx | ERR_BAD_REQUEST |
Request failed with status code 404 |
あり |
| 500 などの 5xx | ERR_BAD_RESPONSE |
Request failed with status code 500 |
あり |
| サーバーが止まっている | ECONNREFUSED |
connect ECONNREFUSED 127.0.0.1:30999 |
なし |
| ホスト名が引けない | ENOTFOUND |
getaddrinfo ENOTFOUND no-such-host.invalid |
なし |
| timeout を超えた | ECONNABORTED |
timeout of 1000ms exceeded |
なし |
同じ+clarifyTimeoutError: true |
ETIMEDOUT |
timeout of 1000ms exceeded |
なし |
URL に // が無い |
ERR_INVALID_URL |
Invalid URL "http:127.0.0.1:30900/api": missing "//" after protocol |
なし(request も無し) |
| キャンセルした | ERR_CANCELED |
canceled |
なし |
| CORS に止められた(jsdom) | ERR_NETWORK |
Network Error |
なし |
| JSON が壊れている(下の設定時) | ERR_BAD_RESPONSE |
Unexpected token 'o', "not json {" is not valid JSON |
あり(status 200) |
サーバーが返したエラーの本文({"error":"not_found",…})は error.response.data に入ります。 error.message は axios が作った文で、サーバーの文言ではありません。画面に出すメッセージは error.response.data から取ります。
4xxを例外にしない(validateStatus)
axios は既定で 2xx 以外を例外にします。 404 を「データが無い」という普通の結果として扱いたいときは、validateStatus で例外にする条件を変えます。
const res = await axios.get(`${B}/users/999`, { validateStatus: (s) => s < 500 });
console.log(res.status, res.data);
404 { error: 'not_found', message: 'user 999 not found' }
JSONが壊れていても成功になる
応答が JSON でないとき(not json { という本文)、既定では例外にならず、res.data に文字列のまま入りました(status=200 data="not json {")。壊れた JSON を例外にしたいときは、README のとおり responseType: 'json' と transitional: { silentJSONParsing: false } を両方指定します。指定すると上の表の最後の行のとおり ERR_BAD_RESPONSE になりました。
ログにトークンを残さない(toJSON と redact)
error.toJSON() は、ログに出しやすい形にします。redact に指定したキーは伏せ字になりました。
try {
await axios.get(`${B}/users/999`, { headers: { Authorization: 'Bearer secret-token' }, redact: ['authorization'] });
} catch (e) {
console.log(e.toJSON().config.headers.Authorization);
}
[REDACTED ****]
toJSON() のキーは message,name,description,number,fileName,lineNumber,columnNumber,stack,config,code,status でした。error をそのまま console.log すると、設定の中のヘッダー(トークン)までログに出るので、外に送るログは toJSON() と redact を使うと安全です。
catchしないとどうなるか
await した axios の失敗を catch しないと、Node.js 22 ではプロセスが止まり、次の表示になりました(先頭の部分)。
AxiosError: Request failed with status code 404
at settle (file:///.../node_modules/axios/lib/core/settle.js:19:12)
at IncomingMessage.handleStreamEnd (file:///.../node_modules/axios/lib/adapters/http.js:1318:13)
JavaScript の try/catch と Promise のエラーの扱いは、関連記事「JavaScriptのasync/awaitの使い方|エラー処理と並列実行」で扱っています。
axiosのタイムアウトとキャンセル(AbortController)
この節では、時間で切る方法と、途中で止める方法、その違いが分かります。
timeout の既定は 0(無制限)です。 README も、本番では timeout を設定するよう注意しています。応答が来ないと、いつまでも待ち続けます。
途中で止めるには、AbortController の signal を渡します(0.22.0 から対応)。3つの止め方を実際に動かしました。
// 1) AbortController で途中で止める
const controller = new AbortController();
const t0 = Date.now();
const p = api.get('/slow?ms=3000', { signal: controller.signal });
setTimeout(() => controller.abort(), 200);
try { await p; } catch (e) {
console.log(`abort(): ${Date.now() - t0}ms code=${e.code} name=${e.name} message=${e.message} isCancel=${axios.isCancel(e)}`);
}
// 2) すでに止めた signal を渡すと、送らずに失敗する
try { await api.get('/users/1', { signal: controller.signal }); } catch (e) {
console.log(`abort 済みの signal: code=${e.code} isCancel=${axios.isCancel(e)}`);
}
// 3) AbortSignal.timeout(全体の時間で切る)
const t1 = Date.now();
try { await api.get('/slow?ms=3000', { signal: AbortSignal.timeout(500) }); } catch (e) {
console.log(`AbortSignal.timeout(500): ${Date.now() - t1}ms code=${e.code} message=${e.message} isCancel=${axios.isCancel(e)}`);
}
abort(): 265ms code=ERR_CANCELED name=CanceledError message=canceled isCancel=true
abort 済みの signal: code=ERR_CANCELED isCancel=true
AbortSignal.timeout(500): 504ms code=ERR_CANCELED message=canceled isCancel=true
2つ目は、擬似 API の記録に GET /api/users/1 が無く、送られずに失敗していました。
| 止め方 | error.code | 向いている場面 |
|---|---|---|
timeout: 5000 |
ECONNABORTED(設定で ETIMEDOUT) |
全リクエストの上限(インスタンスに書く) |
signal: AbortSignal.timeout(5000) |
ERR_CANCELED |
1回だけ別の上限にしたいとき |
signal: controller.signal と abort() |
ERR_CANCELED |
画面を離れた・検索語が変わったとき |
キャンセルはエラーではないので、axios.isCancel(e) で見分けて、エラー表示から外します。 以前の CancelToken は 0.22.0 から非推奨で、README は新しいコードでは使わないよう書いています。
失敗したときの再試行(503・axios-retry)
この節では、一時的な失敗(503 など)を待って送り直す方法が分かります。
axios の設定には再試行の項目がありません。 公式ドキュメント(Retry)もレスポンスのインターセプターで書く方法を載せています。インターセプターで書くか、axios-retry(4.5.0)を使います。
インターセプターで書いた例です。同じ結果になっても困らないメソッド(GET・PUT・DELETE など)だけを、最大3回、Retry-After ヘッダーがあればその秒数だけ待って送り直します。POST は二重登録になるので送り直しません。
const retryApi = axios.create({ baseURL: 'http://127.0.0.1:30900/api' });
retryApi.interceptors.response.use(undefined, async (error) => {
const cfg = error.config;
const status = error.response?.status;
const retriable = !error.response ? error.code !== 'ERR_CANCELED' : [429, 502, 503, 504].includes(status);
cfg._retryCount = cfg._retryCount ?? 0;
if (!retriable || cfg._retryCount >= 3 || !['get', 'head', 'options', 'put', 'delete'].includes(cfg.method)) {
return Promise.reject(error);
}
cfg._retryCount++;
const after = Number(error.response?.headers['retry-after']);
const wait = Number.isFinite(after) ? after * 1000 : 2 ** cfg._retryCount * 200;
console.log(` retry ${cfg._retryCount}: status=${status ?? error.code} ${wait}ms 待って再送`);
await new Promise((r) => setTimeout(r, wait));
return retryApi(cfg);
});
const r = await retryApi.get('/flaky');
擬似 API の /api/flaky は、2回目までは 503(Retry-After: 1)、3回目で 200 を返します。
retry 1: status=503 1000ms 待って再送
retry 2: status=503 1000ms 待って再送
flaky: 2017ms status=200 data={"ok":true,"tries":3}
axios-retry では次のとおりです。既定の条件は「通信エラー、または冪等なリクエスト(GET・HEAD・OPTIONS・PUT・DELETE)の 5xx」です(axios-retry の README)。
import axios from 'axios';
import axiosRetry, { exponentialDelay } from 'axios-retry';
const api = axios.create({ baseURL: 'http://127.0.0.1:30900/api' });
axiosRetry(api, {
retries: 3,
retryDelay: exponentialDelay,
onRetry: (n, err) => console.log(` axios-retry ${n}回目: ${err.response?.status ?? err.code}`),
});
const r = await api.get('/flaky');
axios-retry 1回目: 503
axios-retry 2回目: 503
結果 200 {"ok":true,"tries":3} 2318ms
axios-retry の README には、shouldResetTimeout を指定しない限り、timeout は再試行のたびではなく全体に対する時間として扱われるとあります。
並列リクエスト(Promise.all・allSettled)
この節では、複数の API を同時に呼ぶ書き方と、1件失敗したときの違いが分かります。
axios.all と axios.spread は README で非推奨(deprecated)とされ、代わりに Promise.all を使うよう書かれています。
// 1件でも失敗すると全体が失敗
await Promise.all([api.get('/users/1'), api.get('/users/999'), api.get('/users/2')]);
// 全部の結果を受け取る
const settled = await Promise.allSettled([api.get('/users/1'), api.get('/users/999'), api.get('/users/2')]);
Promise.all: 1件失敗で全体が失敗 → Request failed with status code 404
Promise.allSettled: ok 佐藤 / ng 404 / ok 鈴木
画面の一部だけが失敗しても残りを表示したいときは Promise.allSettled を使います。
TypeScriptでaxiosに型を付ける
この節では、res.data と error に型を付ける書き方と、型の誤りがどう出るかが分かります。
axios は型定義を同梱しています(package.json の types が index.d.ts)。 @types/axios は入れません。メソッドに <型> を付けると、res.data がその型になります。
import axios, { AxiosError, type AxiosInstance } from 'axios';
type User = { id: number; name: string };
type NewUser = Omit<User, 'id'>;
type ApiError = { error: string; message: string };
const api: AxiosInstance = axios.create({ baseURL: 'http://127.0.0.1:30900/api', timeout: 5000 });
// <戻り値の型> を付けると res.data が User になる
export async function getUser(id: number): Promise<User> {
const res = await api.get<User>(`/users/${id}`);
return res.data;
}
// POST は <戻り値, AxiosResponse, 送る本文> の順
export async function createUser(body: NewUser): Promise<User> {
const { data } = await api.post<User, { data: User }, NewUser>('/users', body);
return data;
}
async function main() {
const u = await getUser(1);
console.log('getUser:', u.id, u.name.toUpperCase());
console.log('createUser:', await createUser({ name: '伊藤' }));
try {
await getUser(999);
} catch (e) {
if (axios.isAxiosError<ApiError>(e)) {
// e.response?.data は ApiError | undefined
console.log('isAxiosError:', e.response?.status, e.response?.data.message);
} else {
throw e;
}
}
const err: AxiosError | null = null;
console.log('done', err);
}
main();
tsc(TypeScript 7.0.2、strict: true)の型検査を通り、Node.js 22.22.2 で .ts のまま実行できました。
getUser: 1 佐藤
createUser: { id: 5, name: '伊藤' }
isAxiosError: 404 user 999 not found
done null
わざと誤りを入れたファイルでは、次のエラーが出ました。
const res = await axios.get<User>('/users/1');
console.log(res.data.mail); // 型に無いプロパティ
await axios.post<User, { data: User }, { name: string }>('/users', { name: 1 }); // 本文の型の誤り
try { await axios.get('/x'); } catch (e) { console.log(e.response.status); } // e は unknown
bad.ts(5,24): error TS2339: Property 'mail' does not exist on type 'User'.
bad.ts(6,72): error TS2322: Type 'number' is not assignable to type 'string'.
bad.ts(7,58): error TS18046: 'e' is of type 'unknown'.
catch (e) の e は unknown です。 axios.isAxiosError<ApiError>(e) で絞り込むと、e.response?.data が ApiError として使えます。なお、<User> はコンパイル時の約束で、実行時にサーバーの JSON を検査するものではありません。外部 API の値を信用できないときは、zod などで実行時にも検査します。
使った tsconfig.json です。
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"strict": true,
"noEmit": true,
"types": ["node"]
}
}
README は、TypeScript の moduleResolution を node16(module: "node16" で暗に指定される)にすることを勧め、CJS に出力する場合は esModuleInterop を有効にするよう書いています。
Reactでaxiosを使う(useEffect・キャンセル・StrictMode)
この節では、React のコンポーネントから axios で一覧を取る書き方と、StrictMode で何が起きるかが分かります。
useEffect の中で取り、片付け(cleanup)で abort() します。 画面を離れたあとに、前の通信の応答で state が上書きされるのを防げます。
// src/api.ts
import axios from 'axios';
export const api = axios.create({
baseURL: 'http://127.0.0.1:30900/api',
timeout: 10000,
});
// src/UserList.tsx
import { useEffect, useState } from 'react';
import axios from 'axios';
import { api } from './api.js';
type User = { id: number; name: string };
export function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
api
.get<{ items: User[] }>('/users', { signal: controller.signal })
.then((res) => {
setUsers(res.data.items);
setLoading(false);
})
.catch((e) => {
if (axios.isCancel(e)) {
console.log('[UserList] 前の通信を取り消し:', e.code);
return;
}
setError(axios.isAxiosError(e) ? `${e.code}: ${e.message}` : String(e));
setLoading(false);
});
return () => controller.abort(); // 画面から消えたら通信を止める
}, []);
if (loading) return <p>読み込み中…</p>;
if (error) return <p role="alert">エラー:{error}</p>;
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
React 19.3.1(開発用のビルド)を jsdom の中で <StrictMode> で描画し、画面の HTML を時間ごとに表示しました。ページの URL は http://localhost:30901、擬似 API は Access-Control-Allow-Origin: http://localhost:30901 を返します。
adapter = [ 'xhr', 'http', 'fetch' ] | page origin = http://localhost:30901
[UserList] 前の通信を取り消し: ERR_CANCELED
HTML(50ms) : <p>読み込み中…</p>
HTML(500ms): <ul><li>佐藤</li><li>鈴木</li></ul>
StrictMode の開発時は effect が「実行→片付け→実行」と2回動くので、1回目の通信が ERR_CANCELED で取り消されました。 isCancel で外しているので、エラー表示にはなりません。擬似 API に届いた GET /api/users は1回で、ブラウザと同じく XMLHttpRequest(xhr アダプター)が使われていました。StrictMode で effect が2回動く理由は、関連記事「ReactでAPIデータ取得 fetchとuseEffectの書き方と競合対策」でも扱っています。
ページの URL を http://localhost:30902(擬似 API が許可していない Origin)に変えると、同じコンポーネントは次の表示になりました。
HTML(500ms): <p role="alert">エラー:ERR_NETWORK: Network Error</p>
ブラウザで CORS に止められると、axios には Network Error しか届きません。 README も、ERR_NETWORK はブラウザでは CORS や Mixed Content が原因のことがあり、本当の理由はブラウザのコンソールで確かめるよう書いています。直すのは API サーバー側の CORS の設定です。関連記事「CORSエラーの原因と解決方法|エラーメッセージ別に解説」で扱っています。
キャッシュ・再取得・読み込み中の状態をまとめて扱いたいときは、TanStack Query などのライブラリの中で axios を呼ぶ形も使われます。関連記事「Reactの状態管理の比較と使い分け|Zustand・TanStack Query」で扱っています。
axiosとfetchの違い(同じ処理を書き比べ)
この節では、標準の fetch と axios の違いを、同じ処理を動かした実物で比べられます。
fetch はブラウザと Node.js(18 から標準)に最初からある関数です。同じ擬似 API に fetch で送りました。
// fetch:404 でも例外にならない
const r1 = await fetch(`${B}/users/999`);
console.log(`fetch 404: ok=${r1.ok} status=${r1.status} → 例外なし`, JSON.stringify(await r1.json()));
// fetch:POST は JSON.stringify と Content-Type を自分で
const r2 = await fetch(`${B}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '田中' }),
});
console.log(`fetch POST: ${r2.status}`, JSON.stringify(await r2.json()));
// fetch:オブジェクトをそのまま body に渡すと
const r3 = await fetch(`${B}/echo`, { method: 'POST', body: { name: '田中' } });
console.log('fetch body にオブジェクト:', JSON.stringify((await r3.json()).body));
// fetch:タイムアウト
try { await fetch(`${B}/slow?ms=3000`, { signal: AbortSignal.timeout(500) }); } catch (e) {
console.log(`fetch timeout: name=${e.name} message=${e.message}`);
}
// fetch:接続できない
try { await fetch('http://127.0.0.1:30999/'); } catch (e) {
console.log(`fetch 接続不可: ${e.name}: ${e.message} cause=${e.cause?.code}`);
}
fetch 404: ok=false status=404 → 例外なし {"error":"not_found","message":"user 999 not found"}
fetch POST: 201 {"id":4,"name":"田中"}
fetch body にオブジェクト: "[object Object]"
fetch timeout: name=TimeoutError message=The operation was aborted due to timeout
fetch 接続不可: TypeError: fetch failed cause=ECONNREFUSED
fetch は 404 でも成功として返り、オブジェクトをそのまま body に渡すと [object Object] という文字列が送られました(content-type: text/plain;charset=UTF-8)。fetch では res.ok の確認と JSON.stringify を自分で書きます。
axios
npm で入れる(axios 1.20.0 は約2.2MB+依存)
2xx 以外は例外。JSON の変換は自動
timeout・インターセプター・進捗が設定で使える
fetch
ブラウザと Node.js に標準で入っている
2xx 以外も成功。res.ok と res.json() を自分で
timeout は AbortSignal.timeout で付ける
違いの一覧です。ky(fetch を土台にした小さなライブラリ)も並べました。
| 項目 | axios 1.20.0 | fetch | ky 2.2.0 |
|---|---|---|---|
| 入れ方 | npm(依存あり) | 標準 | npm(Node.js 22 以上) |
| 4xx・5xx | 例外(ERR_BAD_REQUEST など) |
例外にならない(res.ok が false) |
例外(HTTPError) |
| JSON の送受信 | 自動 | JSON.stringify・res.json() を書く |
json: オプション・.json() |
| タイムアウト | timeout(既定 0=無制限) |
AbortSignal.timeout() |
timeout(既定 10000ms) |
| 共通処理 | インターセプター | 関数で包む | hooks |
| 自動の再試行 | なし(axios-retry 等) | なし | あり(既定 2回) |
| アップロードの進捗 | onUploadProgress |
なし | onUploadProgress |
fetch と ky の項目は、それぞれ MDN と ky の README で確かめました。Node.js から送ったときの user-agent は、axios が axios/1.20.0、fetch が node でした。fetch での書き方は、関連記事「JavaScript fetchの使い方|用途別コピペ関数と実行結果」で扱っています。
axios は adapter: 'fetch' で、中の通信を fetch に切り替えることもできます(1.7.0 から)。この場合も 404 は ERR_BAD_REQUEST の例外になりました。
const fa = axios.create({ adapter: 'fetch', baseURL: B });
axios(fetch adapter) 404: code=ERR_BAD_REQUEST status=404
axios(fetch adapter) 200: {"id":1,"name":"佐藤"}
axiosとfetchの選び方
| 状況 | 選ぶものの目安 |
|---|---|
| 呼ぶ API が少なく、依存を増やしたくない | fetch |
| 認証ヘッダー・タイムアウト・エラー処理を全リクエストでそろえたい | axios(インスタンス+インターセプター) |
| アップロードの進捗バーを出したい | axios(onUploadProgress) |
| 一時的な失敗の自動再試行が欲しい | ky、または axios+axios-retry |
| 既存のコードが axios で書かれている | axios のまま(版を上げる) |
axiosのよくあるエラーと直し方
この節では、出たエラー文から原因と直し方を引けます。エラー文は、この記事で実際に出たものです。
| エラー・症状 | 原因 | 直し方 |
|---|---|---|
Cannot find package 'axios' imported from … |
そのフォルダに axios が入っていない | npm install axios。別のフォルダで実行していないか確かめる |
AxiosError: Request failed with status code 404 |
URL が違う、またはデータが無い | getUri() で実際の URL を見る。baseURL と url のつながりを確かめる |
Request failed with status code 401 |
トークンが無い・切れている | Authorization ヘッダーを確かめる。インターセプターで更新して再送 |
Request failed with status code 500 |
サーバー側のエラー | error.response.data とサーバーのログを見る |
AxiosError: Network Error(ブラウザ) |
CORS・Mixed Content・サーバー停止 | ブラウザのコンソールを見る。CORS は API 側で許可する |
connect ECONNREFUSED 127.0.0.1:30999 |
相手のポートで誰も待っていない | サーバーの起動とポート番号を確かめる |
getaddrinfo ENOTFOUND no-such-host.invalid |
ホスト名を引けない | ホスト名の綴りと DNS を確かめる |
timeout of 1000ms exceeded |
timeout 内に応答が無い |
遅い API なら値を延ばす。延ばす前に遅い原因を調べる |
Invalid URL "http:127.0.0.1:30900/api": missing "//" after protocol |
http:// の // が抜けている |
URL を http:// から書く |
canceled(ERR_CANCELED) |
abort した、または StrictMode の片付け | axios.isCancel(e) で見分けてエラー表示から外す |
res.data が文字列のまま |
応答が JSON でない(Content-Type・本文の誤り) |
サーバーの応答を直す。例外にしたいなら silentJSONParsing: false |
| サーバーで JSON として読めない | 自分で JSON.stringify した文字列を渡した |
オブジェクトのまま data に渡す |
'e' is of type 'unknown'(TS18046) |
catch の変数は unknown |
axios.isAxiosError(e) で絞り込む |
axios.all の非推奨の警告・指摘 |
非推奨の API | Promise.all に書き換える |
Network Error と表示されてもサーバーに届いていることがあります。ブラウザの開発者ツールの「ネットワーク」タブで、リクエストが送られたか・応答のヘッダーに Access-Control-Allow-Origin があるかを見ると、CORS かどうかが分かります。
axiosを安全に使うための確認(版・依存・上限)
この節では、axios の版を選ぶときと、外部の API を呼ぶときに確かめることが分かります。
主な版と変更
| 版 | 内容(npm・README・GitHub のリリースで確認) |
|---|---|
| 0.22.0 | AbortController に対応。CancelToken は非推奨に |
| 1.0.0 | ここから semver(版番号の規則)に従う |
| 1.7.0 | fetch アダプターを追加 |
| 1.12.0 | fetch アダプターで独自の fetch を使えるように |
| 1.14.1・0.30.4 | 2026年3月31日に不正な版が公開され、npm から削除 |
| 1.19.0 | form-data の下限を上げる(CRLF の脆弱性への対応) |
| 1.20.0 | 2026年8月26日に npm へ公開。2026年10月22日時点の latest |
0.x 系の最新は 0.34.0(npm の v0x タグ、2026年9月16日公開)です。
2026年3月31日、axios 1.14.1 と 0.30.4 が、悪意のある依存パッケージ(plain-crypto-js)を含む形で npm に公開され、数時間後に削除されました。 今は npm view axios@1.14.1 version が No match found for version 1.14.1 になります。Datadog Security Labs の解説は、^1.13.6 のような範囲指定で、その時間帯に npm install した環境が該当しうるとしています。
- 1lockfile をコミットしているかpackage-lock.json があれば、CI でも同じ版が入る。CI は npm ci を使う
- 2入っている版を確かめたかnpm ls axios と npm why axios で、間接的に入った axios も含めて見る
- 3timeout を設定したか既定の 0 は無制限。インスタンスに上限を書く
- 4応答の大きさに上限を付けたか信頼できない相手を Node.js から呼ぶなら maxContentLength・maxBodyLength を指定する
- 5トークンを axios 本体の既定に入れていないかAPI ごとのインスタンスに付ける。ログは toJSON と redact で伏せる
4つ目について、README は「maxContentLength と maxBodyLength の既定は -1(無制限)」で、圧縮された小さな応答が展開されて大量のメモリを使う場合があるため、信頼できないサーバーを呼ぶときは上限を付けるよう書いています。
const external = axios.create({
timeout: 10000,
maxContentLength: 10 * 1024 * 1024, // 応答は 10MB まで
maxBodyLength: 10 * 1024 * 1024, // 送る本文も 10MB まで
});
よくある質問
Q. axiosの読み方は? A. 日本語では「アクシオス」と読まれることが多いです。README には読み方の記載はありません。
Q. axiosとは何ですか? A. ブラウザと Node.js から HTTP リクエストを送るための、Promise を返す JavaScript ライブラリです。JSON の自動変換、4xx・5xx の例外化、インターセプター、タイムアウトが使えます。ライセンスは MIT です。
Q. axiosとfetchはどちらを使えばいいですか? A. 呼ぶ API が少なく依存を増やしたくなければ fetch、認証・タイムアウト・エラー処理を全リクエストでそろえたいなら axios が書きやすいです。違いは「axiosとfetchの違い」の節の表にあります。
Q. axiosのGETでパラメータを渡すには?
A. axios.get(url, { params: { page: 2 } }) と params に渡します。配列は既定で ids[]=1&ids[]=2 になり、ids=1&ids=2 にしたいときは paramsSerializer: { indexes: null } を指定します。
Q. axiosでPOSTするとき、JSONはどう送りますか?
A. オブジェクトのまま axios.post(url, { name: '佐藤' }) と渡すと、Content-Type: application/json で JSON が送られます。自分で JSON.stringify した文字列を渡すと application/x-www-form-urlencoded になりました。
Q. axiosのヘッダーはどこに書きますか?
A. GET・DELETE は第2引数、POST・PUT・PATCH は第3引数の headers です。全リクエスト共通なら axios.create({ headers }) かインターセプターに書きます。
Q. axiosのタイムアウトの既定値は?
A. 0(無制限)です。timeout: 5000 のようにミリ秒で指定し、超えると timeout of 5000ms exceeded(ECONNABORTED)で失敗します。
Q. axiosのエラーでレスポンスの中身を取るには?
A. error.response.data です。error.response が無ければ応答が届いていないので、error.code(ECONNREFUSED・ECONNABORTED・ERR_NETWORK など)で原因を見ます。
Q. @types/axiosは必要ですか?
A. 不要です。axios は型定義(index.d.ts)を同梱しています。axios.get<User>() で res.data に型が付きます。
Q. axiosのリクエストをキャンセルするには?
A. new AbortController() の signal を渡し、controller.abort() を呼びます。失敗は ERR_CANCELED になり、axios.isCancel(e) で見分けられます。CancelToken は非推奨です。
Q. ReactでaxiosのAPIが2回呼ばれるのはなぜですか?
A. 開発時の StrictMode が effect を2回動かすためです。片付けで abort() しておくと、1回目は取り消され、画面には影響しません。
Q. axiosは安全ですか?脆弱性はありますか?
A. 2026年3月31日に不正な版(1.14.1・0.30.4)が公開され削除された件があります。lockfile をコミットし、npm ls axios で入っている版を確かめます。2026年10月22日時点の最新は 1.20.0 です。
まとめ
- axios は、ブラウザと Node.js で同じ書き方の HTTP クライアント。中ではブラウザは XMLHttpRequest、Node.js は http モジュールを使う。最新は 1.20.0(2026年10月22日時点)。
get(url, config)・post(url, data, config)の引数の順に注意。オブジェクトは JSON で送られ、paramsは URL にエンコードされる(配列は既定でids[]=1)。axios.createでbaseURL・timeoutをまとめる。timeoutの既定は 0(無制限)。- インターセプターでトークンを付け、401 では更新を1回にまとめて送り直す。リクエスト側は後から登録したものが先に動く。
- エラーは
error.response(サーバーが応答した)→error.request(応答が無い)→ どちらも無い(送る前の誤り)の順に見分ける。キャンセルはisCancelで外す。 - fetch との違いは、4xx・5xx の例外、JSON の自動変換、timeout とインターセプター。少ない呼び出しなら fetch、そろえたい処理が多いなら axios。
参考・出典
確認日はいずれも 2026年10月22日です。
- axios「README」(GitHub、v1.x):https://github.com/axios/axios
- axios Docs「はじめに」(日本語):https://axios-http.com/ja/docs/intro
- axios Docs「Request config」:https://axios.rest/pages/advanced/request-config
- axios Docs「Interceptors」:https://axios.rest/pages/advanced/interceptors
- axios Docs「Error handling」:https://axios.rest/pages/advanced/error-handling
- axios Docs「Cancellation」:https://axios.rest/pages/advanced/cancellation
- axios Docs「Authentication」:https://axios.rest/pages/advanced/authentication
- axios Docs「Retry」:https://axios.rest/pages/advanced/retry
- axios Docs「Security」:https://axios.rest/pages/misc/security
- axios「Release v1.20.0」(GitHub):https://github.com/axios/axios/releases/tag/v1.20.0
- npm「axios」:https://www.npmjs.com/package/axios
- npm「axios-retry」:https://www.npmjs.com/package/axios-retry
- npm「ky」:https://www.npmjs.com/package/ky
- npm Docs「package.json(overrides)」:https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides
- Datadog Security Labs「Compromised axios npm package delivers cross-platform RAT」:https://securitylabs.datadoghq.com/articles/axios-npm-supply-chain-compromise
- MDN「Fetch API」:https://developer.mozilla.org/ja/docs/Web/API/Fetch_API
- MDN「AbortSignal: timeout() 静的メソッド」:https://developer.mozilla.org/ja/docs/Web/API/AbortSignal/timeout_static
- MDN「オリジン間リソース共有 (CORS)」:https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
- React「StrictMode」:https://ja.react.dev/reference/react/StrictMode
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「axiosの使い方|GET・POST・エラー処理・インターセプターを実例で解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。