Tips & Tricks

Claude Code के साथ IndexedDB Implementation

Claude Code का उपयोग करके indexeddb implementation सीखें। Practical tips और code examples शामिल हैं।

IndexedDBを選ぶ理由

localStorageは手軽ですが、容量制限(5MB程度)やdata構造の制約があり है।IndexedDBは数百MB以ऊपरのdataを構造化して保存でき、インデックスによるfastsearchもpossible है।Claude Code का उपयोग करके、IndexedDBのcomplexなAPIをラップした使いやすいdataアクセス層 build किया जा सकता है。

型safeなIndexedDBwrapper

> IndexedDBを型safeに操作できるwrapperclassを作って。
> CRUD操作とインデックスsearchにsupportして。
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('databaseが開かれていません');
    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);
    });
  }
}

実際のapplicationでの使用例

// database定義
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(),
});

// カテゴリでsearch
const workNotes = await db.findByIndex('notes', 'by-category', 'work');

serverとのsync

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('sync失敗。オンライン復帰時に再試行し है।');
        break;
      }
    }
  }
}

// オンライン復帰時に自動sync
window.addEventListener('online', () => {
  syncManager.syncToServer('notes', '/api/notes/sync');
});

Reacthookとのintegration

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のcomplexなAPIを型safeにラップし、オフラインsupportやserversync तकefficientlyimplementationでき है।Service WorkerとのintegrationはService Workerutilizationを、cache戦略はcache戦略をदेखें。

IndexedDB APIの仕様はMDN Web Docs - IndexedDBदेखें。

#Claude Code #IndexedDB #localstorage #offline #TypeScript
मुफ़्त

मुफ़्त PDF: 5 मिनट में Claude Code चीटशीट

बस अपना ईमेल दर्ज करें और हम तुरंत A4 एक-पृष्ठ चीटशीट PDF भेज देंगे।

हम आपकी व्यक्तिगत जानकारी की सुरक्षा करते हैं और स्पैम नहीं भेजते।

Masa

लेखक के बारे में

Masa

Claude Code का गहराई से उपयोग करने वाले इंजीनियर। claudecode-lab.com चलाते हैं, जो 10 भाषाओं में 2,000 से अधिक पेजों वाला टेक मीडिया है।