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をご覧ください。
#Claude Code
#IndexedDB
#ローカルストレージ
#offline
#TypeScript
Related Posts
Tips & Tricks
Tips & Tricks
10 Tips to Triple Your Productivity with Claude Code
Learn about 10 tips to triple your productivity using Claude Code. Practical tips and code examples included.
Tips & Tricks
Tips & Tricks
Canvas/WebGL Optimization with Claude Code
Learn about canvas/webgl optimization using Claude Code. Practical tips and code examples included.
Tips & Tricks
Tips & Tricks
Markdown Implementation with Claude Code
Learn about markdown implementation using Claude Code. Practical tips and code examples included.