外部のWeb APIからデータを取得したり、別のサーバーにデータを送ったりするとき、Node.jsでは組み込みのfetchが使えます。Node.js 18以降ではfetchが標準で搭載されているため、node-fetchやaxiosといったライブラリをインストールしなくても、ブラウザと同じ感覚でHTTPリクエストを送れます。
注意したいのが、fetchは404や500などのHTTPエラーでも例外(エラー)を投げないことです。ネットワーク自体に失敗したときだけエラーになり、「ページが見つからない」などのレスポンスは正常な結果として返ってきます。これを知らないと、エラーを見逃してしまいます。この記事では、実機のNode.jsで実際にAPIを叩きながら、fetchの使い方を整理します。
- Node.js 18以降は
fetchが組み込み。インストール不要です。 - 基本は
const res = await fetch(url)→const data = await res.json()。 - 404や500でも例外は投げられません。
res.okやres.statusで成否を確認します。 - POSTは
fetch(url, { method, headers, body: JSON.stringify(...) })。 - レスポンスのbody(
.json()や.text())は一度しか読めません。 - ネットワークエラーに備えて
try-catchで囲みます。
逆にAPIサーバーを作る側はExpressで最小のAPIサーバーを作る、低レベルな通信はhttpモジュール、APIキーを環境変数で渡すならprocess完全ガイドもあわせて参考になります。
fetchの基本(GET + JSON)
もっとも基本的な使い方は、await fetch(url)でレスポンスを受け取り、await res.json()でJSONをオブジェクトに変換します。fetchは非同期なのでawaitを付けます。
// async関数の中、またはトップレベルawaitで実行
const res = await fetch("https://api.github.com/repos/nodejs/node", {
headers: { "User-Agent": "my-app" }, // GitHub APIはUser-Agentが必要
});
const data = await res.json(); // JSONをオブジェクトに変換
console.log(res.status); // 200
console.log(data.name); // node
console.log(data.language); // JavaScript
実機でも、GitHub APIにアクセスしてres.statusが200、data.nameがnode、data.languageがJavaScriptと、JSONが正しく取得・解析できました。fetchはPromiseを返すためawaitで待ち、返ってきたres(Responseオブジェクト)の.json()でボディをJSONとして読み取ります。.json()自体も非同期なのでawaitが必要です。なお、多くのAPIはUser-Agentヘッダーが無いとリクエストを拒否するため、付けておくと安全です。
【最重要】404/500でもthrowしない(res.okを確認)
ここがfetchでもっとも誤解されやすい点です。fetchは、サーバーが404や500を返しても例外を投げません。リクエスト自体は「成功」として扱われ、エラーかどうかはres.ok(200番台ならtrue)やres.statusで自分で確認する必要があります。
const res = await fetch("https://api.github.com/repos/nodejs/not-exist-xyz", {
headers: { "User-Agent": "my-app" },
});
// 404でも throw されない! ここに来る
console.log(res.status); // 404
console.log(res.ok); // false(200番台のときだけ true)
// 必ず res.ok を確認する
if (!res.ok) {
throw new Error(`APIエラー: ${res.status}`);
}
const data = await res.json();
実機で確認したところ、存在しないリポジトリにアクセスしてもfetchは例外を投げず、res.statusが404、res.okがfalseという結果が普通に返ってきました。つまり、try-catchで囲んでいても、404や500はcatchに入りません。fetchが例外を投げるのは、ネットワークに繋がらない・URLが不正・タイムアウトなど、リクエストが届かなかったときだけです。サーバーが返したエラー(404・500など)を検知するには、必ずres.ok(またはres.status)を自分でチェックしてください。これを忘れると、エラーレスポンスのHTMLやエラーJSONを正常なデータとして処理してしまい、バグの原因になります。axiosなどのライブラリはHTTPエラーで自動的に例外を投げるので、この点がfetchとの大きな違いです。
POSTでJSONを送る
データを送信するPOSTでは、第2引数のオプションでmethod・headers・bodyを指定します。JSONを送るときはbodyをJSON.stringify()で文字列にし、Content-Typeヘッダーを付けるのがポイントです。
const res = await fetch("https://example.com/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json", // JSONを送る宣言
},
body: JSON.stringify({ name: "田中", age: 30 }), // 文字列にして渡す
});
if (!res.ok) {
throw new Error(`失敗: ${res.status}`);
}
const result = await res.json();
bodyには文字列を渡すため、オブジェクトをJSON.stringify()でJSON文字列に変換します。そしてContent-Type: application/jsonを付けることで、サーバーに「JSONを送っている」と伝えます。これを忘れると、サーバー側(たとえばExpressのexpress.json())がボディを正しく解析できません。PUTやDELETEも同様にmethodを変えるだけで送れます。
レスポンスのbodyは一度だけ
注意点として、レスポンスのボディ(res.json()やres.text())は一度しか読めません。同じレスポンスに対して2回読もうとするとエラーになります。
const res = await fetch(url); const data = await res.json(); // 1回目: OK // const again = await res.json(); // 2回目: TypeError(bodyは消費済み) // JSONかテキストか、どちらか一方だけを読む // const text = await res.text(); // .json() と両方は呼べない
実機で確認したところ、同じレスポンスに対してres.json()を2回呼ぶとTypeErrorになりました(ボディは一度読むと消費されるためです)。res.json()とres.text()も、両方を呼ぶことはできません。レスポンスがJSONならres.json()、プレーンテキストやHTMLならres.text()、画像などのバイナリならres.arrayBuffer()と、用途に応じてどれか一つを選びます。実機でも、テキストファイルをres.text()で取得すると、ファイルの中身が文字列で得られることを確認しました。レスポンスの中身を複数回使いたいときは、最初に変数へ受け取っておきます。
エラー処理(try-catch + res.ok)
実務では、ネットワークエラー(try-catch)とHTTPエラー(res.ok)の両方に対応します。この2つを組み合わせるのが、fetchの正しいエラー処理です。
async function getUser(id) {
try {
const res = await fetch(`https://api.example.com/users/${id}`);
// HTTPエラー(404・500など)を自分で検知する
if (!res.ok) {
throw new Error(`HTTPエラー: ${res.status}`);
}
return await res.json();
} catch (err) {
// ネットワークエラー or 上で投げたHTTPエラー
console.error("取得失敗:", err.message);
return null;
}
}
このように、try-catchでネットワークの失敗を捕まえつつ、if (!res.ok)でサーバーが返したエラーを検知してthrowします。投げたエラーは同じcatchでまとめて処理できます。この形をテンプレートとして覚えておくと、fetchを安全に使えます。
ヘッダーと認証トークン
多くのAPIは、認証のためにAPIキーやトークンをヘッダーに付ける必要があります。headersにAuthorizationを追加します。キーはコードに直書きせず、環境変数から読み込むのが安全です。
const token = process.env.API_TOKEN; // 環境変数から読む(直書きしない)
const res = await fetch("https://api.example.com/me", {
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
});
APIキーやトークンをコードに直接書くと、GitHubなどに公開したときに漏洩します。process.env.API_TOKENのように環境変数から読み込み、キー自体はコードに含めないようにします。Bearer ${token}の形式は、多くのAPIで使われる認証ヘッダーの書き方です。
主なポイントまとめ
fetchの要点をまとめます。
| 項目 | 書き方・注意 |
|---|---|
| GET | const res = await fetch(url) |
| JSON取得 | const data = await res.json() |
| 成否の確認 | res.ok / res.status(404でもthrowしない) |
| POST | { method, headers, body: JSON.stringify(...) } |
| テキスト取得 | await res.text()(jsonと両方は不可) |
| 認証 | headers: { Authorization: ... } |
| エラー処理 | try-catch + res.okチェック |
よくある失敗
404・500をtry-catchで捕まえようとする
fetchはHTTPエラーで例外を投げません。res.okを自分でチェックします。
res.json()を2回呼ぶ
bodyは一度しか読めません。json()とtext()もどちらか一方だけです。
POSTでJSON.stringifyを忘れる
bodyは文字列です。オブジェクトはJSON.stringify()で変換します。
Content-Typeを付け忘れる
JSONを送るときはContent-Type: application/jsonを付けないと、相手が解析できません。
APIキーをコードに直書きする
漏洩の原因です。process.envなど環境変数から読み込みます。
よくある質問
fetchが標準で組み込まれているため、インストール不要です。node-fetchやaxiosを入れなくても、そのままawait fetch(url)でリクエストを送れます。古いバージョンのNode.jsではnode-fetchなどのライブラリが必要です。fetchはHTTPエラー(404・500など)では例外を投げない仕様です。ネットワーク自体の失敗だけがcatchに入ります。404などを検知するには、if (!res.ok)やres.statusを自分でチェックして、必要ならthrowしてください。res.json()を2回呼んだか、res.json()とres.text()を両方呼んだ可能性があります。レスポンスのボディは一度読むと消費されるため、読めるのは一度だけです。中身を複数回使うなら、最初に変数へ受け取っておきます。{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(データ) }を渡します。bodyは文字列でなければならないため、オブジェクトはJSON.stringify()でJSON文字列に変換します。process.env.API_TOKENのように環境変数から読み込み、キー自体はコードに含めないでください。.envファイルと環境変数の管理を組み合わせるのが一般的です。まとめ
- Node.js 18以降は
fetchが組み込み。await fetch(url)→await res.json()が基本です。 - 404・500では例外が投げられないため、
res.okで必ず成否を確認します。 - POSTは
method・headers・JSON.stringifyしたbodyを指定します。 - レスポンスのbodyは一度だけ。
json()かtext()のどちらか一方を使います。 - エラー処理は
try-catch+res.ok、APIキーは環境変数から読み込みます。
Node.jsのfetchは、追加ライブラリなしでAPIを叩ける手軽な手段です。最大の注意点である「HTTPエラーで例外を投げない」さえ押さえれば、安全にデータの取得・送信ができます。res.okのチェックを習慣にして、外部APIとの連携を実装してみてください。

