3秒でわかる
ブラウザに標準で入っている HTTP 通信の関数。Promise を返すので、API からデータを取ってきて画面に反映する処理を短く書けます。
30秒図解
もう少し詳しく
どういうものか
fetch はブラウザ標準の関数で、指定した URL へ HTTP リクエストを送り、レスポンスを Promise で返します。ライブラリを入れなくても使えます。Node.js も 18 以降は同じ関数を持っています。
戻り値は Response オブジェクトで、本文はまだ読み込まれていません。JSON として使いたい場合は res.json() をもう一度待つ、という二段構えになります。
なぜ必要か
画面を再読み込みせずにサーバーからデータを取り、必要な部分だけ書き換えるためです。商品一覧の絞り込み、投稿の送信、入力途中の候補表示など、今のウェブアプリの動きはほとんどがこの通信の上に成り立っています。
具体例
// 取得する
async function loadUsers() {
const res = await fetch("/api/users");
if (!res.ok) {
throw new Error(`取得に失敗しました status=${res.status}`);
}
const users = await res.json();
return users;
}
// 送信する
async function createUser(name) {
const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name }),
});
return res.json();
}つまずきやすいところ
最大の落とし穴は、404 や 500 が返ってきても Promise が失敗扱いにならないことです。通信そのものが成立していれば成功として解決されるため、res.ok か res.status を自分で確認しないと、エラーページの中身を JSON として読もうとして別の場所で落ちます。
body を二度読むこともできません。res.json() を呼んだあとに res.text() を呼ぶとエラーになります。ログに残したいなら先に text で受けて自分でパースします。
POST で Content-Type を付け忘れると、サーバー側でボディが空に見えることがあります。逆に FormData を送るときは自分で Content-Type を書いてはいけません。境界文字列ごとブラウザが組み立てるためです。
別のドメインへ送る場合は CORS の制限を受け、サーバー側が許可ヘッダーを返していないとブラウザがレスポンスを渡してくれません。Cookie を一緒に送りたい場合は credentials の指定も要ります。
覚え方
「一度目の await で封筒が届き、二度目の await で中身を開ける」と考えると、二段階の理由が腑に落ちます。