IndexedDB Implementation with Claude Code
Learn about indexeddb implementation using Claude Code. Practical tips and code examples included.
IndexedDBを選ぶ理由
localStorageは手軽ですが、容量制限(5MB程度)やデータ構造の制約があります。IndexedDBは数百MB以上のデータを構造化して保存でき、インデックスによる高速検索も可能です。Claude Codeを使えば、IndexedDBの複雑なAPIをラップした使いやすいデータアクセス層を構築できます。
型安全なIndexedDBラッパー
> IndexedDBを型安全に操作できるラッパークラスを作って。
> CRUD操作とインデックス検索に対応して。
interface StoreConfig {
name: string;
keyPath: string;
indexes?: { name: string; keyPath: string; unique?: boolean }[];
}
class Database {
private db: IDBDatabase | null = null;
constructor(
private name: string,
private version: number,
private stores: StoreConfig[]
) {}
async open(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.name, this.version);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
for (const store of this.stores) {
if (!db.objectStoreNames.contains(store.name)) {
const objectStore = db.createObjectStore(store.name, { keyPath: store.keyPath });
store.indexes?.forEach((idx) => {
objectStore.createIndex(idx.name, idx.keyPath, { unique: idx.unique });
});
}
}
};
request.onsuccess = () => { this.db = request.result; resolve(); };
request.onerror = () => reject(request.error);
});
}
private getStore(name: string, mode: IDBTransactionMode) {
if (!this.db) throw new Error('データベースが開かれていません');
return this.db.transaction(name, mode).objectStore(name);
}
async put<T>(storeName: string, data: T): Promise<void> {
return new Promise((resolve, reject) => {
const request = this.getStore(storeName, 'readwrite').put(data);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
async get<T>(storeName: string, key: IDBValidKey): Promise<T | undefined> {
return new Promise((resolve, reject) => {
const request = this.getStore(storeName, 'readonly').get(key);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getAll<T>(storeName: string): Promise<T[]> {
return new Promise((resolve, reject) => {
const request = this.getStore(storeName, 'readonly').getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async delete(storeName: string, key: IDBValidKey): Promise<void> {
return new Promise((resolve, reject) => {
const request = this.getStore(storeName, 'readwrite').delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
async findByIndex<T>(storeName: string, indexName: string, value: IDBValidKey): Promise<T[]> {
return new Promise((resolve, reject) => {
const index = this.getStore(storeName, 'readonly').index(indexName);
const request = index.getAll(value);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
実際のアプリケーションでの使用例
// データベース定義
const db = new Database('myApp', 1, [
{
name: 'notes',
keyPath: 'id',
indexes: [
{ name: 'by-category', keyPath: 'category' },
{ name: 'by-updated', keyPath: 'updatedAt' },
],
},
]);
// 初期化
await db.open();
// ノートの保存
await db.put('notes', {
id: crypto.randomUUID(),
title: '会議メモ',
content: '本日の議題...',
category: 'work',
updatedAt: Date.now(),
});
// カテゴリで検索
const workNotes = await db.findByIndex('notes', 'by-category', 'work');
サーバーとの同期
class SyncManager {
private db: Database;
constructor(db: Database) {
this.db = db;
}
async syncToServer(storeName: string, apiEndpoint: string) {
const pending = await this.db.findByIndex<any>(storeName, 'by-sync', 'pending');
for (const item of pending) {
try {
await fetch(apiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});
await this.db.put(storeName, { ...item, syncStatus: 'synced' });
} catch {
console.log('同期失敗。オンライン復帰時に再試行します。');
break;
}
}
}
}
// オンライン復帰時に自動同期
window.addEventListener('online', () => {
syncManager.syncToServer('notes', '/api/notes/sync');
});
Reactフックとの統合
function useIndexedDB<T>(storeName: string, key?: IDBValidKey) {
const [data, setData] = useState<T | T[] | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
const load = key
? db.get<T>(storeName, key)
: db.getAll<T>(storeName);
load.then(setData).finally(() => setLoading(false));
}, [storeName, key]);
return { data, loading };
}
Summary
Claude Codeを使えば、IndexedDBの複雑なAPIを型安全にラップし、オフライン対応やサーバー同期まで効率的に実装できます。Service Workerとの連携はService Worker活用を、キャッシュ戦略はキャッシュ戦略を参照してください。
IndexedDB APIの仕様はMDN Web Docs - IndexedDBをご覧ください。
Free PDF: Claude Code Cheatsheet in 5 Minutes
Just enter your email and we'll send you the single-page A4 cheatsheet right away.
We handle your data with care and never send spam.
Level up your Claude Code workflow
50 battle-tested prompt templates you can copy-paste into Claude Code right now.
About the Author
Masa
Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.
Related Posts
7 CLAUDE.md Templates for Claude Code You Can Copy Into Real Projects
Copy-paste 7 practical CLAUDE.md templates for solo apps, content sites, APIs, teams, and legacy codebases, plus the failure cases to avoid.
Claude Code Approval and Sandbox Guide | Safe Daily Settings for Real Work
Learn how to split Claude Code actions into allow, ask, deny, and sandboxed workflows with working settings, hooks, and rollout examples.
Complete Beginner's Guide to Claude Code 2026 | 7 Steps from Zero to Production-Ready
A complete beginner's guide for first-time Claude Code users. From installation to integrating it into your real development workflow — covering every pitfall Masa ran into when starting out.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.