はじめに
IndexedDB は、ブラウザにデータを保存するための強力な仕組みです。IndexedDBの詳細については、過去記事「データ保存技術/IndexedDBとは」を参照してください。
IndexedDB は、非常に便利ではありますが、標準 API は、イベント駆動で扱いにくく、次のような問題があります。
- トランザクションの寿命が短い
- 非同期処理が複雑(Promise ではなくイベント)
- カーソル操作が冗長
- エラーハンドリングが難しい
そこで登場するのが idb (公式サイト)です。
idb は、Google の Jake Archibald さんが作った IndexedDB の Promise ラッパー で、標準 API の複雑さをほぼ完全に隠してくれます。
👉 IndexedDB を “モダンに” 使うなら、idb 一択です。
開発者の方、関係者の方には、この場をお借りして、深くお礼を申し上げたいと思います。
idb の import
idb を利用するためには、ライブラリーの読み込みが必要です。下記に読み込み例を示します。
① CDN で読み込む(最も簡単)
<script type="module">
import { openDB } from "https://cdn.jsdelivr.net/npm/idb@7/build/esm/index.js";
</script>
② npm でインストール(本格的なプロジェクト向け)
npm install idb
import { openDB } from "idb";
👉 idb の中心は openDB()。これさえ理解すれば、IndexedDB の 80% は扱えます。
openDB の基本
最小の DB を開くコードは次のとおりです。
const db = await openDB("MyDB", 1, {
upgrade(db) {
db.createObjectStore("users", { keyPath: "id" });
}
});
openDB の引数
| 引数 | 意味 |
|---|---|
| 第1引数 | DB 名 |
| 第2引数 | バージョン番号 |
| 第3引数 | スキーマ変更時に呼ばれる upgrade 関数 |
upgrade の役割
- オブジェクトストアの作成
- インデックスの作成
- スキーマ変更(バージョンアップ)
👉 標準の IndexedDB の onupgradeneeded と同じですが、Promise で扱えるため圧倒的に書きやすいです。
トランザクションの操作
標準の IndexedDB では、トランザクションの寿命が短く、非同期処理を挟むとすぐエラーになります。idb では、これが ほぼ自動管理 されます。
idb のトランザクションは自動
基本的に、idb のトランザクションは自動(必要なときだけ 開く)で行われます。
await db.put("users", { id: 1, name: "Kon" });
これだけで内部的に:
- readwrite トランザクション開始
- オブジェクトストア取得
- put 実行
- commit
が自動で行われます。
尚、下記の様に、明示的に使うことも可能です。
const tx = db.transaction("users", "readwrite");
await tx.store.put({ id: 1, name: "Kon" });
await tx.done;
👉 標準の IndexedDB API より圧倒的に安全で扱いやすいです。
オブジェクトストアの操作
オブジェクトストアの生成や、インデックスの作成、検索といった操作も直観的でわかりやすいです。
作成
upgrade(db) {
db.createObjectStore("users", { keyPath: "id" });
}
インデックスの作成
upgrade(db) {
const store = db.createObjectStore("users", { keyPath: "id" });
store.createIndex("by_name", "name");
}
インデックス検索
const users = await db.getAllFromIndex("users", "by_name", "Kon");
👉 単純な操作であれば、標準の IndexedDB API の複雑なカーソル操作なく、アクセスが可能です。
CRUD 操作(idb 版)
読み書きの操作も、非常にシンプルです。
Create(追加)
await db.add("users", { id: 1, name: "Kon" });
Read(取得)
const user = await db.get("users", 1);
Update(更新)
await db.put("users", { id: 1, name: "Updated" });
Delete(削除)
await db.delete("users", 1);
全件取得
const all = await db.getAll("users");
👉 標準の IndexedDB API の 操作に必要な 10 行のコードが、idb なら 1 行で済みます。
カーソル(Cursor)操作について
カーソルとは、IndexedDB のレコードを 順番に走査する仕組み です。
データをストリーミング的に1行ずつ処理できる為、大量データであっても、破綻することなく処理が可能です。
標準の IndexedDB API のカーソル操作は、少し複雑ですが、idb はそのカーソル操作をシンプルにしてくれます。
標準の IndexedDB API
const tx = db.transaction("users", 'readonly');
const store = tx.objectStore("users");
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
console.log(cursor.key, cursor.value);
cursor.continue();
}
idb
let cursor = await db.transaction("users").store.openCursor();
while (cursor) {
console.log(cursor.key, cursor.value);
cursor = await cursor.continue(); // 次のレコードへ進む
}
👉 標準の IndexedDB API より圧倒的に簡潔に記述できます。
まとめ
- IndexedDB を “モダンに” 使うなら idb 一択
openDB()が中心で、Promise ベースで扱える。- トランザクションは自動管理されるため安全
- CRUD 操作は 1 行で書ける。
- 中小規模のデータなら
getAll()で十分 - 大量データではカーソルが必須
- idb はカーソル操作をシンプルにしてくれる。