知っておきたいWeb API — fetch・Storage・IntersectionObserver
この章の目次開く
- Web APIとは — ブラウザが提供する機能
- fetch API — HTTP通信を行う
- HTTPエラーは自動では例外にならない
- JSONをPOSTする
- よく使うinitオプション
- Request / Response — fetchの入出力
- AbortController — fetchを中断する
- Storage API — localStorageとsessionStorage
- オブジェクトはJSON文字列にして保存する
- Storageは同期API
- CookieStore API — JavaScriptからCookieを読み書きする
- document.cookieとの違い
- IntersectionObserver — 要素が見えたかを検知する
- 監視を止める
- MutationObserver — DOMの変更を監視する
- そのほかよく使うWeb API
- URLとURLSearchParams
- Clipboard API
- Web Storage以外の保存先
- 早見表
- よくあるハマりどころ
- 1. fetchの404をcatchで拾えると思い込む
- 2. response.json()を2回呼ぶ
- 3. Storageにオブジェクトをそのまま保存する
- 4. localStorageに認証トークンを入れる
- 5. cookieStoreでセッションIDを管理する
- 6. Observerを解除しない
- ちゃんと使うためのポイント
- 参考リンク
ブラウザには、JavaScript本体とは別にWeb APIが用意されています。この章で扱うのは、HTTP通信を行う fetch、ブラウザに小さなデータを保存する localStorage / sessionStorage、JavaScriptからCookieを読み書きする cookieStore、要素が画面に入ったかを検知する IntersectionObserver、DOMの変更を監視する MutationObserver です。
この章では、アプリ開発で使う頻度が高いWeb APIを、用途・構文・戻り値・注意点まで整理します。Promise やDOM操作の知識と組み合わせて読むと、ブラウザ上で「データを取得し、保存し、必要なタイミングで画面を更新する」流れがつながります。
Web APIとは — ブラウザが提供する機能
Web APIは、ブラウザがJavaScriptから呼び出せる形で提供している機能群です。JavaScriptの文法だけではHTTP通信も、画面の要素取得も、ブラウザへの保存もできません。そこをつなぐのがWeb APIです。
// JavaScriptの標準機能
const names = ['Taro', 'Hanako'].map((name) => name.toUpperCase());
// ブラウザのWeb API
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log('clicked');
});
代表的なWeb APIは次の通りです。
| API | 何をするか | よく使う場面 |
|---|---|---|
fetch | HTTPリクエストを送る | APIからJSONを取得、フォーム送信 |
localStorage | ブラウザに永続保存する | テーマ設定、簡単な入力途中データ |
sessionStorage | タブを閉じるまで保存する | 一時的な画面状態、確認画面の入力値 |
cookieStore | JavaScriptからCookieを読み書きする | テーマや言語設定など、サーバーにも送る小さな値 |
IntersectionObserver | 要素が画面に入ったか検知する | 遅延読み込み、無限スクロール、表示時アニメーション |
MutationObserver | DOMの追加・削除・属性変更を検知する | 外部スクリプトが書き換えるDOMの監視 |
URL / URLSearchParams | URLを安全に組み立てる | クエリ文字列の読み書き |
navigator.clipboard | クリップボードを読み書きする | コピーボタン、招待URLのコピー |
fetch API — HTTP通信を行う
fetch はHTTPリクエストを送るためのWeb APIです。戻り値は Promise<Response> なので、await してレスポンスを受け取り、さらに response.json()、response.text()、response.blob() で本文を読み取ります。
構文: fetch(input, init?)
| 引数 | 説明 |
|---|---|
input | URL文字列、または Request オブジェクト |
init(省略可) | method、headers、body、credentials、signal を指定する設定オブジェクト |
戻り値: Promise<Response>
const response = await fetch('/api/users');
const users = await response.json();
console.log(users);HTTPエラーは自動では例外にならない
fetch がrejectするのは、ネットワーク不通、CORSエラー、リクエストの中断により、レスポンスを受け取れなかった場合です。サーバーが 404 や 500 を返した場合、fetch 自体は成功扱いになります。
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}JSONをPOSTする
JSONを送るときは、method、headers、body を指定します。body にオブジェクトを直接渡すのではなく、JSON.stringify で文字列に変換します。
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Taro',
role: 'admin',
}),
});
if (!response.ok) {
throw new Error(`Failed to create user: ${response.status}`);
}
const result = await response.json();
学習者body に { name: 'Taro' } をそのまま渡しちゃダメなんですか?
fetch の body は送信するデータそのものです。JSON APIに送る場合は文字列化されたJSONが必要なので、JSON.stringify を通します。JSONの変換ルールはJSONの基礎と実践を参照してください。
よく使うinitオプション
| オプション | 役割 | 例 |
|---|---|---|
method | HTTPメソッド | 'GET', 'POST', 'PUT', 'DELETE' |
headers | HTTPヘッダー | { 'Content-Type': 'application/json' } |
body | 送信本文 | JSON.stringify(data) |
credentials | Cookie送信の扱い | 'include', 'same-origin', 'omit' |
signal | 中断用のシグナル | controller.signal |
cache | キャッシュ制御 | 'no-store', 'reload' |
Request / Response — fetchの入出力
fetch はURL文字列だけでなく、Request オブジェクトも受け取れます。レスポンス側は Response オブジェクトとして返ってきます。
const request = new Request('/api/users', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
const response = await fetch(request);Response には、ステータスやヘッダー、本文を読むためのメソッドがあります。
| プロパティ / メソッド | 役割 |
|---|---|
response.ok | ステータスが 200 〜 299 なら true |
response.status | ステータスコード |
response.statusText | ステータス文言 |
response.headers | レスポンスヘッダー |
response.json() | 本文をJSONとして読み、JavaScriptの値にする |
response.text() | 本文を文字列として読む |
response.blob() | 本文をBlobとして読む |
const response = await fetch('/api/profile');
console.log(response.status); // 200
console.log(response.headers.get('content-type')); // "application/json; charset=utf-8"
const profile = await response.json();AbortController — fetchを中断する
AbortController は、実行中の処理に「中断してよい」というシグナルを渡すためのWeb APIです。fetch では signal オプションに渡すことで、リクエストを途中でキャンセルできます。
構文: new AbortController()
| プロパティ / メソッド | 役割 |
|---|---|
controller.signal | fetch に渡す中断シグナル |
controller.abort() | 中断を通知する |
const controller = new AbortController();
const request = fetch('/api/search?q=javascript', {
signal: controller.signal,
});
// もう結果が不要になったら中断する
controller.abort();
try {
await request;
} catch (error) {
if (error.name === 'AbortError') {
console.log('request was aborted');
} else {
throw error;
}
}検索入力のたびにAPIを呼ぶ画面では、古いリクエストを中断すると「遅れて返ってきた古い結果で画面が上書きされる」事故を防げます。
Storage API — localStorageとsessionStorage
Storage APIは、ブラウザにキーと値を保存するAPIです。localStorage と sessionStorage は同じメソッドを持ちますが、保存期間が違います。
| API | 保存期間 | 使う場面 |
|---|---|---|
localStorage | 明示的に削除するまで残る | テーマ、表示設定、最後に開いたタブ |
sessionStorage | タブを閉じるまで残る | 入力途中の一時データ、ページ遷移中だけ必要な値 |
構文: storage.setItem(key, value) / storage.getItem(key)
| メソッド | 役割 |
|---|---|
setItem(key, value) | 文字列を保存する |
getItem(key) | 文字列を取得する。存在しない場合は null |
removeItem(key) | 指定キーを削除する |
clear() | そのオリジンのストレージを全削除する |
key(index) | 指定位置のキー名を取得する |
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
console.log(theme); // "dark"
localStorage.removeItem('theme');オブジェクトはJSON文字列にして保存する
Storageに保存できる値は文字列だけです。オブジェクトや配列を保存するときは JSON.stringify、取り出すときは JSON.parse を使います。
const settings = {
theme: 'dark',
sidebarOpen: true,
};
localStorage.setItem('settings', JSON.stringify(settings));
const saved = localStorage.getItem('settings');
const parsed = saved === null ? null : JSON.parse(saved);Storageは同期API
localStorage.getItem() や setItem() は同期的に動きます。大量データを頻繁に読み書きするとメインスレッドを止めます。保存するのは小さな設定値や軽い一時データに限定します。大きなデータや検索可能なデータを保存する場合は、IndexedDBを使います。
CookieStore API — JavaScriptからCookieを読み書きする
Cookie Store APIは、ブラウザが用意した cookieStore インスタンス からCookieを読み書きするWeb APIです。Map や Date のように new CookieStore() で自分で作るのではなく、ページに最初から存在するグローバルオブジェクトを使います。
従来は document.cookie に "name=value; path=/" のような文字列を代入して操作していました。文字列の組み立てとパースが面倒で、非同期処理とも相性が悪かったため、Promiseベースの cookieStore が追加されました。
構文: グローバルの cookieStore をそのまま使う
| メソッド | 戻り値 | 役割 |
|---|---|---|
get(name) | Promise<CookieListItem | null> | 指定名のCookieを取得する |
set(name, value) / set(...) | Promise<undefined> | Cookieを設定する |
delete(name) / delete(...) | Promise<undefined> | Cookieを削除する |
getAll() | Promise<CookieListItem[]> | すべてのCookieを取得する |
await cookieStore.set({
name: 'theme',
value: 'dark',
expires: Date.now() + 86400000,
path: '/',
});
const theme = await cookieStore.get('theme');
console.log(theme?.value); // "dark"
await cookieStore.delete('theme');set には名前と値だけを渡す短い形もあります。
await cookieStore.set('lang', 'ja');取得結果の CookieListItem には name、value、domain、path、expires などのプロパティがあります。存在しないCookieを get した場合は null が返ります。
document.cookieとの違い
document.cookie | cookieStore | |
|---|---|---|
| 戻り値 | セミコロン区切りの文字列 | Promise でオブジェクトを返す |
| 書き込み | 文字列を代入する | set() で属性をオブジェクト指定できる |
| 削除 | 期限切れ日時を過去にするなど間接的 | delete() で明示的に削除できる |
| 変更の監視 | 自前でポーリングが必要 | change イベントで検知できる |
cookieStore.addEventListener('change', (event) => {
for (const { name, value } of event.changed) {
console.log('changed', name, value);
}
for (const { name } of event.deleted) {
console.log('deleted', name);
}
});IntersectionObserver — 要素が見えたかを検知する
IntersectionObserver は、対象要素が画面や親要素の表示領域に入ったかを検知するAPIです。スクロールイベントを自前で監視して座標計算するより、軽く正確に書けます。
構文: new IntersectionObserver(callback, options?)
| 引数 | 説明 |
|---|---|
callback | 交差状態が変わったときに呼ばれる関数 |
options.root | 基準にするスクロール領域。省略時はビューポート |
options.rootMargin | 判定領域の余白。例: '200px 0px' |
options.threshold | どの割合見えたら通知するか。0 〜 1 |
const target = document.querySelector('#load-more');
const observer = new IntersectionObserver(
(entries) => {
const entry = entries[0];
if (entry.isIntersecting) {
console.log('load next page');
}
},
{
root: null,
rootMargin: '200px 0px',
threshold: 0,
},
);
observer.observe(target);rootMargin: '200px 0px' は、画面に入る200px手前で通知する設定です。画像の遅延読み込みや無限スクロールでは、実際に見えてから動くより少し早めに処理を始める方が自然です。

監視を止める
一度だけ処理したい場合は、処理後に unobserve します。監視自体をすべて止める場合は disconnect します。
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
});
document.querySelectorAll('.fade-in').forEach((element) => {
observer.observe(element);
});| メソッド | 役割 |
|---|---|
observe(element) | 要素の監視を開始する |
unobserve(element) | 指定要素の監視を止める |
disconnect() | すべての監視を止める |
MutationObserver — DOMの変更を監視する
MutationObserver は、DOMツリーの変更を検知するAPIです。要素が追加された、属性が変わった、テキストが変わった、といった変更を監視できます。
構文: new MutationObserver(callback)
| 引数 | 説明 |
|---|---|
callback | DOM変更が記録されたときに呼ばれる関数 |
const target = document.querySelector('#comments');
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
console.log('DOM children changed');
}
}
});
observer.observe(target, {
childList: true,
subtree: true,
});observe の第2引数で、何を監視するかを明示します。
| オプション | 監視する変更 |
|---|---|
childList | 子要素の追加・削除 |
attributes | 属性の変更 |
characterData | テキストノードの変更 |
subtree | 子孫要素まで監視する |
attributeFilter | 監視する属性名を絞る |
observer.observe(target, {
attributes: true,
attributeFilter: ['aria-expanded', 'hidden'],
});そのほかよく使うWeb API
URLとURLSearchParams
URLを文字列結合で作ると、?、&、エンコード漏れで壊れます。クエリ文字列は URL と URLSearchParams で扱います。
const url = new URL('/api/search', location.origin);
url.searchParams.set('q', 'JavaScript 入門');
url.searchParams.set('page', '1');
console.log(url.toString());
// "https://example.com/api/search?q=JavaScript+%E5%85%A5%E9%96%80&page=1"const params = new URLSearchParams(location.search);
const page = Number(params.get('page') ?? '1');Clipboard API
navigator.clipboard.writeText() でテキストをクリップボードへコピーできます。HTTPS上で、クリックまたはキーボード操作をきっかけに実行します。
const button = document.querySelector('#copy');
button.addEventListener('click', async () => {
await navigator.clipboard.writeText(location.href);
});Web Storage以外の保存先
ブラウザ内保存には複数の選択肢があります。
| 保存先 | 特徴 |
|---|---|
| Cookie | HTTPリクエストに自動で付く。認証やサーバー連携向き |
cookieStore | JavaScriptからPromiseでCookieを読み書きする |
localStorage | 文字列の小規模保存。JavaScriptから読める |
sessionStorage | タブ単位の一時保存 |
| IndexedDB | 大きな構造化データを非同期に保存できる |
| Cache Storage | Service Workerと組み合わせてレスポンスをキャッシュする |
早見表
| やりたいこと | 使うもの |
|---|---|
| APIからJSONを取得する | fetch(url) → response.ok → response.json() |
| JSONをPOSTする | fetch(url, { method: 'POST', headers, body: JSON.stringify(data) }) |
| fetchを中断する | AbortController と signal |
| 小さな設定値を保存する | localStorage.setItem(key, value) |
| タブを閉じるまで値を保存する | sessionStorage |
| オブジェクトをStorageに保存する | JSON.stringify / JSON.parse |
| JavaScriptからCookieを読む | await cookieStore.get(name) |
| JavaScriptからCookieを書く | await cookieStore.set({ name, value, ... }) |
| 要素が表示領域に入ったら処理する | IntersectionObserver |
| DOMの追加・削除を検知する | MutationObserver |
| クエリ文字列を作る・読む | URL / URLSearchParams |
| テキストをコピーする | navigator.clipboard.writeText(text) |
よくあるハマりどころ
1. fetchの404をcatchで拾えると思い込む
404 や 500 はHTTPレスポンスとして返ってきているため、fetch はrejectしません。response.ok を確認し、失敗時は自分で throw します。
2. response.json()を2回呼ぶ
レスポンス本文はストリームです。await response.json() を呼ぶと本文は消費されます。同じレスポンスでログ用と処理用に2回読む設計は避けます。
3. Storageにオブジェクトをそのまま保存する
localStorage.setItem('user', user) のようにオブジェクトを渡すと、意図したJSONにはなりません。保存前に JSON.stringify(user)、取得後に JSON.parse(text) を使います。
4. localStorageに認証トークンを入れる
StorageはJavaScriptから読めます。XSSが起きると保存した値を盗まれます。秘密情報の保存先として使いません。
5. cookieStoreでセッションIDを管理する
cookieStore はJavaScriptから読めるCookieを操作するAPIです。localStorage と同様、XSSで値を盗まれる可能性があります。ログイン状態の管理には、サーバーが設定する HttpOnly Cookieを使います。
6. Observerを解除しない
不要になった監視を放置すると、意図しない処理やメモリ使用につながります。一度だけの処理なら unobserve、画面を離れるときは disconnect します。
ちゃんと使うためのポイント
- Web APIはブラウザが提供する機能。JavaScript標準機能とは分けて考える
fetchはPromise<Response>を返す。HTTPエラーはresponse.okで判定する- JSON送信では
Content-Type: application/jsonとJSON.stringifyをセットにする Responseの本文は基本的に1回だけ読むlocalStorage/sessionStorageは文字列だけを保存する。オブジェクトはJSONに変換する- Storageに秘密情報を入れない
cookieStoreはグローバルインスタンスをawaitで操作する。HttpOnlyCookieには触れないIntersectionObserverは表示検知、MutationObserverはDOM変更検知に使う- 監視が不要になったら
unobserveまたはdisconnectで止める
次の章では、数値とMath を扱います。Web APIから受け取った数値を丸める、範囲内に収める、ランダムに選ぶ、といった処理を整理します。
