ページを開いた後で、サーバから追加のデータを受け取るには fetch を使います。通信には時間がかかるので、fetch は Promise を返します(11 章)。await で待ちます。
スクリプト
const res = await fetch("samples/api/message.txt");
console.log(res.status, res.ok);
const text = await res.text();
console.log(text.trim());
200 true
サーバから届いた文字です
fetch が返すのはレスポンス(Response)。status は状態の番号(200 は成功、404 は見つからない)、ok は 200 番台なら trueawait して読む。文字なら res.text()、JSON なら res.json()type="module" のスクリプトでは、一番外側で await を書ける(12 章)この章の例が読んでいるファイルは docs/samples/api にあります。手元で試すときは、14 章のローカルサーバで、このフォルダを返すようにします。
Web の API の多くは、データを JSON で返します(05 章)。res.json() は、JSON の文字列をオブジェクトや配列に変えて返します。
body の中身
<ul id="list"></ul>
スクリプト
const res = await fetch("samples/api/products.json");
const products = await res.json();
const list = document.querySelector("#list");
for (const p of products) {
const li = document.createElement("li");
li.textContent = `${p.title}: ${p.price} 円`;
list.append(li);
}
console.log(`${products.length} 件を表示しました`);
3 件を表示しました
受け取ったデータを画面に入れるときは、textContent を使います(15 章の XSS の注意)。
関係のない通信は、Promise.all で同時に始めます(11 章)。
スクリプト
const [products, message] = await Promise.all([
fetch("samples/api/products.json").then((r) => r.json()),
fetch("samples/api/message.txt").then((r) => r.text()),
]);
console.log(products.map((p) => p.title).join("・"));
console.log(message.trim());
りんご・メロン・みかん
サーバから届いた文字です
fetch の失敗には 2 種類あります。扱い方が違うので注意します。
| 失敗 | 例 | fetch の結果 |
|---|---|---|
| サーバが「だめ」と返した | 404 見つからない ・ 500 サーバの不具合 | 成功として返る。res.ok が false |
| 通信そのものができなかった | ネットワークが切れている ・ CORS で止められた | 失敗(TypeError: Failed to fetch) |
404 でも fetch は失敗しません。res.ok を確かめて、だめなら自分で throw します。この確かめを関数にまとめておくと、どこからでも使えます。
スクリプト
const res = await fetch("samples/api/nothing.json");
console.log(res.ok, res.status);
async function getJson(url) {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`読み込みに失敗しました: ${res.status}`);
}
return res.json();
}
try {
await getJson("samples/api/nothing.json");
} catch (e) {
console.log(e.message);
}
false 404
読み込みに失敗しました: 404
通信には時間がかかることがあります。読み込み中であることを表示し、二重に押されないようにボタンを止め、終わったら finally で元に戻します(10 章)。
body の中身
<button id="load">商品を読み込む</button>
<p id="status-text"></p>
スクリプト
async function getJson(url) {
const res = await fetch(url);
if (!res.ok) throw new Error(`読み込みに失敗しました: ${res.status}`);
return res.json();
}
const button = document.querySelector("#load");
const statusText = document.querySelector("#status-text");
button.addEventListener("click", async () => {
button.disabled = true;
statusText.textContent = "読み込み中…";
try {
const products = await getJson("samples/api/products.json");
statusText.textContent = `${products.length} 件ありました`;
} catch (e) {
statusText.textContent = `失敗: ${e.message}`;
} finally {
button.disabled = false;
}
console.log(statusText.textContent);
});
button.click();
3 件ありました
サーバにデータを送るときは、fetch の 2 つ目の引数で、送り方(method)と中身(body)を指定します。JSON で送るときは、JSON.stringify で文字列にし、中身の種類を content-type で伝えます。
const res = await fetch("https://api.example.com/orders", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ productId: 1, quantity: 3 }),
});
if (!res.ok) throw new Error(`送信に失敗しました: ${res.status}`);
const result = await res.json();
この例は、受け取る側のサーバが必要なので、このページでは動かせません。
| method | 意味 |
|---|---|
GET | 受け取る(何も書かなければこれ) |
POST | 新しく作る ・ 送る |
PUT ・ PATCH | 変える |
DELETE | 消す |
ヒント: 通信の中身は、開発者ツールの Network タブで確かめられます(14 章)。送ったものと返ってきたもの、状態の番号、かかった時間が見えます。思ったように動かないときは、まずここを見ます。
ブラウザは安全のため、ページと違う場所(オリジン)のサーバから、JavaScript でデータを読むことを、原則として止めます。オリジンは「https」「lightspeedc.com」「ポート番号」の組です。1 つでも違えば別のオリジンです。
別のオリジンから読んでよいかは、サーバ側が Access-Control-Allow-Origin という見出し(ヘッダー)で答えます。許可されていないと、Console に次のようなエラーが出て、JavaScript には TypeError: Failed to fetch が届きます。
Access to fetch at 'https://api.example.com/data' from origin 'https://lightspeedc.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
この仕組みを CORS(オリジン間リソース共有)と呼びます。ブラウザ側の JavaScript では解決できません。使いたい API が許可していないときは、自分のサーバを経由して取ってくるなど、サーバ側で対応します。
ここが大事: CORS のエラーは「コードの書き間違い」ではなく「サーバが許可していない」というお知らせです。調べるときは、相手の API の説明に、ブラウザから直接使えるか(CORS に対応しているか)が書いてあるかを見ます。