JSONとデータ形式 — APIでやりとりするデータの基本
この章の目次開く
- JSONの基本構文
- JSONとJavaScriptオブジェクトの違い
- JSONのデータ型
- JavaScriptでのJSON操作
- JSON.parse() — 文字列をオブジェクトに
- JSON.stringify() — オブジェクトを文字列に
- fetchでJSONを扱う
- GETでJSONを受け取る
- POSTでJSONを送る
- response.json() は一度しか呼べない
- JSON以外のデータ形式
- なぜJSONが勝ったのか
- よくあるハマりどころ
- 1. 日付の扱い
- 2. 数値の精度
- 3. Content-Typeの不一致
- 実務で使うfetchのパターン
- 基本: エラーハンドリング付きfetch
- タイムアウトの設定
- なぜタイムアウトが重要か
- リトライの実装
- DevToolsでJSONレスポンスを確認する
- Consoleでのデバッグ
- まとめ
Web APIでやりとりするデータ形式として、現在最も広く使われているのが JSON(JavaScript Object Notation) です。名前にJavaScriptとありますが、言語に依存しない汎用的なデータ形式で、あらゆるプログラミング言語で扱えます。
この章では、JSONの基本的な構文、データ型、実務でのハマりどころを整理します。
学習者JSONって何となく使ってるけど、JavaScriptのオブジェクトと何が違うの?日付とかも入れていいの?
JSONの基本構文
JSONは キーと値のペア で構成されるテキスト形式です。
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"age": 28,
"isAdmin": false,
"tags": ["frontend", "react"],
"address": {
"city": "Tokyo",
"zip": "100-0001"
}
}構文のルールはシンプルです。
- キーは ダブルクォート で囲む(シングルクォートは不可)
- 文字列は ダブルクォート で囲む
- 末尾のカンマ(trailing comma)は 不可
- コメントは 書けない
// NG — JSONでよくある構文エラー
{
'name': 'Alice', // シングルクォートはダメ
"age": 28, // この行のカンマは問題ない
"isAdmin": false, // ← 末尾のカンマ(trailing comma)はダメ
}JSONのデータ型
JSONで使えるデータ型は6つだけです。
| データ型 | 例 | 備考 |
|---|---|---|
| 文字列 | "hello" | ダブルクォート必須 |
| 数値 | 42, 3.14 | 整数も小数も同じ |
| 真偽値 | true, false | 小文字のみ |
| null | null | 値がないことを表す |
| 配列 | [1, 2, 3] | 順序付きリスト |
| オブジェクト | {"key": "value"} | キーと値のペア |
JSONに 存在しない型 として、undefined、Date、関数、BigInt、NaN、Infinity があります。これらを JSON.stringify() すると消えたり null に変換されたりします。
JSON.stringify({ a: undefined, b: NaN, c: new Date() })
// '{"b":null,"c":"2024-01-15T09:00:00.000Z"}'
// ← undefinedは消え、NaNはnull、Dateは文字列にJavaScriptでのJSON操作
JSON.parse() — 文字列をオブジェクトに
const text = '{"name": "Alice", "age": 28}';
const data = JSON.parse(text);
console.log(data.name); // "Alice"JSON.stringify() — オブジェクトを文字列に
const user = { name: 'Alice', age: 28 };
const json = JSON.stringify(user);
console.log(json); // '{"name":"Alice","age":28}'
// 見やすく整形する(デバッグ用)
const pretty = JSON.stringify(user, null, 2);
fetchでJSONを扱う
ブラウザの fetch APIでJSONを送受信する典型的なパターンです。
GETでJSONを受け取る
const response = await fetch('/api/users/1');
const user = await response.json(); // レスポンスボディをJSONとしてパース
console.log(user.name);POSTでJSONを送る
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // ← これが必要
},
body: JSON.stringify({
name: 'Alice',
email: 'alice@example.com',
}),
});
const created = await response.json();JSON以外のデータ形式
JSONが主流ですが、場面によっては他の形式も使われます。
| 形式 | 特徴 | 使われる場面 |
|---|---|---|
| JSON | 軽量・人間にも読みやすい | Web APIの標準 |
| XML | タグベース・冗長だが表現力が高い | SOAP API、設定ファイル、レガシーAPI |
| FormData | フォーム送信の形式 | ファイルアップロード |
| Protocol Buffers | バイナリ形式・高速・型安全 | gRPC、マイクロサービス間通信 |
| CSV | カンマ区切りテキスト | データエクスポート、バッチ処理 |
// JSON
{"name": "Alice", "age": 28}
// XML
<user><name>Alice</name><age>28</age></user>
// URL-encoded (FormData)
name=Alice&age=28
よくあるハマりどころ
1. 日付の扱い
JSONには日付型がないため、文字列として表現します。ISO 8601形式が標準です。
{
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2024-01-15T12:30:00.000Z"
}受け取る側で new Date() に変換する必要があります。
const data = await response.json();
const createdAt = new Date(data.createdAt);2. 数値の精度
JSONの数値は浮動小数点で表現されるため、非常に大きな整数は精度が落ちます。
JSON.parse('{"id": 9007199254740993}');
// → { id: 9007199254740992 } ← 1ずれるIDに大きな整数を使うAPIでは、文字列として返すことがあります。
3. Content-Typeの不一致
サーバーが Content-Type: text/html でJSONを返す、またはクライアントが Content-Type を付けずにJSONを送る、という不一致でパースが失敗するケースがあります。送信側と受信側でContent-Typeを揃えることが基本です。
実務で使うfetchのパターン
ここまでの知識を組み合わせて、実務で使えるfetchのパターンを整理します。
基本: エラーハンドリング付きfetch
素の fetch はHTTPエラーでも例外を投げないため、ラッパー関数を作っておくとコードがすっきりします。
async function apiFetch(url, options = {}) {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(
error.message || `API Error: ${response.status}`
);
}
// 204 No Content はボディがない
if (response.status === 204) return null;
return response.json();
}
// 使い方
try {
const user = await apiFetch('/api/users/1');
console.log(user.name);
} catch (err) {
console.error('取得に失敗:', err.message);
}タイムアウトの設定
fetch にはデフォルトのタイムアウトがありません。AbortController を使って設定します。
async function fetchWithTimeout(url, options = {}, timeoutMs = 10000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
return response;
} catch (err) {
if (err.name === 'AbortError') {
throw new Error(`リクエストが${timeoutMs}msでタイムアウトしました`);
}
throw err;
} finally {
clearTimeout(timeoutId);
}
}リトライの実装
ネットワークエラーや5xxエラーは一時的な場合が多いため、リトライが有効です。
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// 5xxはリトライ対象、4xxはリトライしない
if (response.status >= 500 && attempt < maxRetries) {
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
continue;
}
return response;
} catch (err) {
// ネットワークエラーはリトライ
if (attempt === maxRetries) throw err;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
}Math.pow(2, attempt) で 指数バックオフ(1秒 → 2秒 → 4秒)を実装しています。固定間隔でリトライすると、サーバーが回復する前に大量のリクエストが殺到する「サンダーリングハード」問題が起きます。
DevToolsでJSONレスポンスを確認する
NetworkタブでAPIリクエストを選択し、Response タブまたは Preview タブを見ると、JSONレスポンスの中身が整形されて表示されます。
Previewタブではオブジェクトをツリー形式で展開でき、大きなレスポンスでも目的のフィールドを素早く探せます。
Consoleでのデバッグ
DevToolsのConsoleで fetch を直接実行して、APIの動作を確認することもできます。
// Consoleから直接APIを叩いて確認
const res = await fetch('/api/users');
const data = await res.json();
console.table(data); // テーブル形式で見やすく表示console.table() は配列のデータをテーブル形式で表示するので、APIレスポンスの確認に便利です。
まとめ
- JSONは キーと値のペア で構成されるテキスト形式。Web APIの標準フォーマット
- キーはダブルクォート必須、末尾カンマ不可、コメント不可
JSON.parse()で文字列→オブジェクト、JSON.stringify()でオブジェクト→文字列fetchでJSONを送る際はContent-Type: application/jsonを明示する- JSONには日付型がないため、ISO 8601文字列 で表現するのが標準
この本の後半(準備中)では、認証と認可、CORSとセキュリティ、APIのテストとツール、GraphQLなどを扱う予定です。