IndexedDB

Объектная NoSQL-БД в браузере: гигабайты структурированных данных, транзакции, индексы.

Что это

Транзакционная key-value/object-store БД, асинхронная, на основе callback'ов и событий (не Promise — старое API). Хранит JS-объекты, Blob'ы, файлы. Каждый origin изолирован. Размер — в gigabyte-range. Pain в работе → обёрток типа idb, Dexie, localForage почти всегда лучше.

Базовое использование (без обёртки)

// 1. Открыть БД (создаст если нет, версия — для миграций)
const request = indexedDB.open('podcasts', 1);

request.onupgradeneeded = (e) => {
  const db = e.target.result;
  // создаём store с keyPath
  const store = db.createObjectStore('episodes', { keyPath: 'guid' });
  store.createIndex('byPubDate', 'pubDate', { unique: false });
};

request.onsuccess = (e) => {
  const db = e.target.result;
  // 2. Транзакция
  const tx = db.transaction('episodes', 'readwrite');
  const store = tx.objectStore('episodes');

  store.put({ guid: 'ep-1', title: 'First', pubDate: Date.now() });
  store.get('ep-1').onsuccess = (e) => console.log(e.target.result);

  tx.oncomplete = () => console.log('tx done');
};

request.onerror = (e) => console.error(e.target.error);

С обёрткой idb (Promise-based)

import { openDB } from 'idb';

const db = await openDB('podcasts', 1, {
  upgrade(db) {
    db.createObjectStore('episodes', { keyPath: 'guid' });
  },
});

await db.put('episodes', { guid: 'ep-1', title: 'First' });
const ep = await db.get('episodes', 'ep-1');
const all = await db.getAll('episodes');
await db.delete('episodes', 'ep-1');

Структура

  • Database → версия → миграция через onupgradeneeded
  • Object Store → как таблица (но без схемы), у объекта keyPath
  • Index → дополнительный путь для поиска (не только по keyPath)
  • Transaction → атомарный, readonly/readwrite/versionchange
  • Cursor → итерация по записям

Поддержка

Все современные браузеры. Доступно из Window, Worker и Service Worker.

Подводные камни

  • API на callback'ах — без обёртки больно
  • Транзакция автокоммитится при возврате в event loop → нельзя await внутри транзакции после .net await (только sync шаги или tx.done)
  • Версии — целые числа, миграция через onupgradeneeded только при увеличении версии
  • Safari исторически глючил с большими записями
  • Не сериализуется через JSON.stringify (Blob → потеря); используй родной структурированный клон

vs альтернативы

Когда
IndexedDB Много структурированных записей, нужны индексы
OPFS Большие файлы (видео, БД-файлы), sync API в Worker
localStorage Несколько мелких настроек (до ~5 MB)
Cookies Нужно отправлять на сервер автоматически

Используется в bootcamp

  • Podcast Player — хранение метаданных эпизодов, прогресса прослушивания
  • AsyncRace — БД гаража (можно простую через IDB вместо localStorage)

Ссылки

🎓 Источники

См. также