IndexedDB は「ブラウザ内のちゃんとしたデータベース」
localStorage / sessionStorage は「小さなメモ帳」でした。
IndexedDB は一段レベルが違います。
イメージとしては、「ブラウザの中にある本格的なデータベース」 です。
大量のデータを保存できる
オブジェクトをそのまま保存できる(文字列だけじゃない)
検索用のインデックスを張れる
非同期 API(Promise ベース)で UI を止めにくい
「本気でデータを扱いたいときのブラウザ内ストレージ」が IndexedDB です。
ただし、そのぶん API はかなり複雑 です。
最初から全部を理解しようとせず、「何ができるのか」「どんな場面で使うのか」から掴んでいきましょう。
localStorage と何が違うのかを先に押さえる
データ量と性能の違い
localStorage
文字列だけ
容量は数 MB 程度
同期 API(重い処理をすると UI が止まる)
IndexedDB
オブジェクトをそのまま保存できる
容量はもっと大きい(ブラウザ依存だが、localStorage よりずっと多い)
非同期 API(Promise / イベントで結果を受け取る)
「ちょっとした設定」なら localStorage で十分ですが、
「たくさんのデータ」「検索」「更新」「削除」をちゃんとやりたいなら IndexedDB の出番です。
データの構造の違い
localStorage
キーと文字列だけ。
構造を持たせたいなら自分で JSON にして管理する必要がある。
IndexedDB
データベース → オブジェクトストア(テーブルのようなもの) → レコード(オブジェクト)
という階層構造を持ちます。
オブジェクトストアごとに「主キー」を決めて、
そこにオブジェクトを保存していきます。
// 例えばこんなオブジェクトをそのまま保存できる
const user = {
id: 1,
name: "taro",
age: 20,
};
JavaScriptJSON.stringify しなくても、そのまま保存できます。
ここが localStorage との大きな違いです。
IndexedDB のざっくり構造イメージ
データベース → オブジェクトストア → レコード
頭の中でこうイメージしてください。
データベース(DB)
そのサイト専用の「データの箱」。名前とバージョンを持つ。
オブジェクトストア(Object Store)
DB の中にある「テーブル」のようなもの。
例えば users ストア、todos ストア、など。
レコード(Record)
オブジェクトストアの中に入っている一つ一つのデータ。
主キー(id など)で識別される。
例えば、こんな感じです。
- データベース:
"myAppDB"- オブジェクトストア:
"users"- レコード:
{ id: 1, name: "taro" } - レコード:
{ id: 2, name: "hanako" }
- レコード:
- オブジェクトストア:
"todos"- レコード:
{ id: 1, text: "牛乳を買う", done: false }
- レコード:
- オブジェクトストア:
localStorage だと「キー → 文字列」だけでしたが、
IndexedDB だと「ストアごとにオブジェクトをたくさん保存できる」イメージです。
主キー(keyPath)のイメージ
オブジェクトストアを作るときに、
「このストアでは、このプロパティを主キーとして使う」と決めます。
例えば users ストアなら id を主キーにする、など。
// イメージ
const user = { id: 1, name: "taro" };
JavaScriptこのとき、id が重複していると保存できません。
主キーは「そのストアの中で一意な値」です。
主キーを指定しないで「自動採番」にすることもできます。
その場合は、保存するときに自動で ID が振られます。
具体例:簡単な「ユーザー一覧」を IndexedDB に保存する流れ
ここでは、あくまで「流れのイメージ」を掴むことを目的にします。
実際のコードは少し長くなりますが、要所だけ見ていきましょう。
データベースを開く(なければ作る)
IndexedDB は非同期なので、indexedDB.open を使います。
const request = indexedDB.open("myAppDB", 1);
JavaScript第1引数がデータベース名、第2引数がバージョンです。
バージョンは「スキーマ変更(ストア追加など)」のときに使います。
open の結果はイベントで返ってきます。
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 初回作成時やバージョンアップ時に呼ばれる
if (!db.objectStoreNames.contains("users")) {
db.createObjectStore("users", { keyPath: "id" });
}
};
request.onsuccess = (event) => {
const db = event.target.result;
// ここで db を使って読み書きしていく
};
request.onerror = (event) => {
console.error("IndexedDB を開けませんでした", event);
};
JavaScriptここで重要なのは、
onupgradeneeded の中で「ストアの定義」を行うonsuccess の中で「実際の読み書き」を行う
という役割分担です。
データを追加する(add / put)
データを書き込むには「トランザクション」を作ります。
function addUser(db, user) {
const tx = db.transaction("users", "readwrite");
const store = tx.objectStore("users");
const request = store.add(user);
request.onsuccess = () => {
console.log("ユーザーを追加しました");
};
request.onerror = (event) => {
console.error("追加に失敗しました", event);
};
}
JavaScriptここでやっていることは、
トランザクションを開始する
対象のオブジェクトストアを取得するstore.add(user) でレコードを追加する
という流れです。
add は「主キーが重複していたら失敗」put は「存在すれば更新、なければ追加」
という違いがあります。
データを読み込む(get / getAll)
1件だけ取得する。
function getUser(db, id) {
const tx = db.transaction("users", "readonly");
const store = tx.objectStore("users");
const request = store.get(id);
request.onsuccess = () => {
const user = request.result;
console.log("取得したユーザー:", user);
};
}
JavaScript全部取得する(ブラウザによっては getAll が使えます)。
function getAllUsers(db) {
const tx = db.transaction("users", "readonly");
const store = tx.objectStore("users");
const request = store.getAll();
request.onsuccess = () => {
const users = request.result;
console.log("全ユーザー:", users);
};
}
JavaScriptここでのポイントは、
「結果はすぐには返ってこない。イベント(または Promise)で受け取る」
という非同期の感覚です。
IndexedDB の「強み」と「難しさ」
強み:本格的なデータベースとして使える
大量のデータを扱える
オブジェクトをそのまま保存できる
インデックスを張って高速検索できる
トランザクションで「まとめて成功 or まとめて失敗」ができる
例えば、
オフライン対応のアプリ(後でまとめて同期する)
大量のキャッシュ(検索結果、記事データなど)
クライアント側での本格的なデータ管理
こういった場面では、localStorage では力不足で、
IndexedDB が本命になります。
難しさ:API が複雑で、非同期前提
イベントベースの API(onsuccess / onerror)
トランザクションの概念
バージョン管理(onupgradeneeded)
初心者がいきなり生の IndexedDB を触ると、
「何をしているのか分からない長いコード」になりがちです。
実務では、
Promise ラッパーやライブラリ(Dexie.js など)を使って、
もう少し扱いやすくすることが多いです。
ただ、
「なぜライブラリが必要なのか」を理解するためにも、
「IndexedDB は非同期で、DB / ストア / トランザクションという構造を持つ」
という概要だけは押さえておく価値があります。
初心者として IndexedDB で本当に掴んでほしいこと
最後に、あなたの頭の中に残しておいてほしいポイントを整理します。
localStorage / sessionStorage は「小さなメモ帳」
IndexedDB は「ブラウザ内のちゃんとしたデータベース」
違いとしては、
オブジェクトをそのまま保存できる(文字列だけじゃない)
大量のデータを扱える
非同期 API(UI を止めにくい)
DB → オブジェクトストア → レコードという構造を持つ
そして、
API は正直かなり複雑
でも「本気でデータを扱うならここに来る」
という立ち位置です。
今の段階では、
「localStorage ではきつくなってきたら、次の選択肢として IndexedDB がある」
「IndexedDB は非同期で、本格的な DB っぽい構造を持っている」
この2つが頭に入っていれば十分です。
その上で、
「じゃあ実際にどう書くか」を学ぶときに、
今日のイメージが土台になってくれます。

