Gernar
Frontend DeveloperBrowser API

Интервью-вопрос

Что такое IndexedDB

IndexedDB - асинхронное клиентское хранилище для структурированных данных, индексов и транзакций. В ответе важно показать, когда оно полезно, а где добавляет сложность или риск потери данных.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 60 правильно

Что ответить про назначение IndexedDB?

Вы хотите дать короткое определение без лишней лекции.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

IndexedDB нужен, когда приложению мало простого строкового хранилища. Это браузерная база данных для объектов. Можно создать хранилища, выбрать ключ, добавить индексы и читать данные асинхронно.

Хороший короткий ответ на интервью:

IndexedDB - это асинхронное клиентское хранилище для структурированных данных в браузере. Оно подходит для оффлайн-режима, кэша данных приложения, черновиков и больших коллекций, где нужны индексы и транзакции. Но его надо использовать осознанно. Есть миграции схемы, ошибки записей, квоты и риск, что локальные данные будут удалены.

Не ограничивайтесь фразой "это база в браузере". Добавьте, что это меняет для фронтенда. UI не должен считать запись успешной до результата операции, а критичные данные нельзя держать только локально.

Чем это отличается от простых хранилищ

localStorage и sessionStorage удобны для маленьких строковых значений. Например, для флага, темы или простой настройки. Но они синхронные и работают со строками. Если хранить там большие JSON-объекты, парсинг и запись могут блокировать главный поток, а структура быстро станет хрупкой.

IndexedDB хранит значения через structured clone. Можно сохранять обычные объекты, массивы, даты, Blob и другие поддерживаемые типы без ручного превращения всего в строку. Плюс можно делать индексы по полям, а не проходить всю коллекцию руками.

Практический вывод простой. Если данные маленькие и простые, не усложняйте. Если нужны оффлайн-данные, поиск, много записей или бинарные данные, IndexedDB выглядит хорошим кандидатом.

Как выбрать хранилище

1Нужно сохранить пару небольших строковых настроек?
Обычно достаточно localStorage или cookie, если это связано с HTTP.
2Нужны большие структурированные данные, поиск по полям или черновики?
Выбирайте IndexedDB. В нем есть object stores, индексы и асинхронные операции.
3Нужно кэшировать HTTP-ответы и статические ресурсы для PWA?
Смотрите в сторону Cache API. IndexedDB оставьте для метаданных и данных приложения.
4Данные критичны и не должны потеряться?
Нужен сервер или синхронизация. IndexedDB используйте как локальную копию, а не единственный источник истины.

Как выглядит работа с базой

В IndexedDB есть важный жизненный цикл. Сначала вы открываете базу по имени и версии. Если версия выросла, браузер вызывает onupgradeneeded. Там можно создать object stores и индексы. После успешного открытия вы работаете с данными через transaction.

function openAppDb() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open("app-db", 1);

    request.onupgradeneeded = () => {
      const db = request.result;

      if (!db.objectStoreNames.contains("drafts")) {
        const store = db.createObjectStore("drafts", { keyPath: "id" });
        store.createIndex("updatedAt", "updatedAt");
      }
    };

    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
    request.onblocked = () => reject(new Error("Database upgrade is blocked by another tab"));
  });
}

async function saveDraft(draft) {
  const db = await openAppDb();
  const tx = db.transaction("drafts", "readwrite");
  const store = tx.objectStore("drafts");

  store.put({ ...draft, updatedAt: Date.now() });

  return new Promise((resolve, reject) => {
    tx.oncomplete = () => {
      db.close();
      resolve();
    };
    tx.onerror = () => {
      db.close();
      reject(tx.error);
    };
    tx.onabort = () => {
      db.close();
      reject(tx.error);
    };
  });
}

В этом примере важен не синтаксис, а контракт. Схема создается в upgrade flow, запись идет внутри transaction, а вызывающий код получает ошибку, если данные не сохранились. Соединение закрывается после операции, чтобы не мешать будущему upgrade в другой вкладке. Без этого легко показать пользователю "черновик сохранен", хотя запись упала.

В React или Next.js не вызывайте такой код во время render и не ждите от SSR доступа к локальной базе пользователя. Запускайте чтение после маунта или по действию пользователя. Показывайте loading и проверяйте, что ответ не устарел, прежде чем обновлять state.

Главные ловушки для frontend-кода

Первая ловушка - считать IndexedDB вечным и надежным хранилищем. Браузер может ограничить место, пользователь может очистить данные сайта, а приватный режим и мобильные браузеры могут вести себя строже. Поэтому для важных сущностей нужна синхронизация с сервером или хотя бы понятный статус "сохранено локально".

Вторая ловушка - забыть, что API асинхронный и событийный. Если вы делаете optimistic UI, заранее решите, что произойдет при ошибке. Например, откатить карточку, показать retry, оставить черновик в очереди или подсветить конфликт.

Третья ловушка - обновить UI результатом старого чтения. Пользователь мог уйти на другой экран, выбрать другой документ или начать новый запрос. В компоненте нужен флаг актуальности, отмена на уровне вашего слоя данных или проверка id перед setState. Иначе IndexedDB создаст такой же race condition, как медленный сетевой запрос.

Четвертая ловушка - путать IndexedDB с Cache API. Для PWA они часто дополняют друг друга. Cache API хранит HTTP responses и хорошо работает с service worker. IndexedDB удобнее для доменных данных, метаданных, очередей отправки и поиска.

Безопаснее
  1. 1Открыть базу нужной версии
  2. 2Создать transaction с понятным режимом
  3. 3Выполнить все операции над store внутри transaction
  4. 4Обработать success, complete, error и abort
  5. 5Показать UI только после подтвержденного результата или сделать rollback
Опасно
  1. 1Считать, что add всегда сработает
  2. 2Обновить UI до результата без плана отката
  3. 3Игнорировать quota и constraint errors
  4. 4Менять схему без повышения версии базы
  5. 5Хранить важные данные без синхронизации

Что сказать про безопасность и квоты

IndexedDB не делает данные секретными. Если в приложении есть XSS, вредный скрипт может получить доступ к тем же браузерным API, что и ваш код. Поэтому хранение токена в IndexedDB не является полноценной защитой. На интервью лучше сказать спокойно. Безопасность зависит от всей модели, включая защиту от XSS, CSP, cookie strategy и серверные ограничения.

С квотами тоже не стоит называть одно точное число для всех браузеров. Правильнее сказать, что лимиты зависят от браузера, устройства, свободного места и режима хранения. Для крупных данных нужно обрабатывать ошибку записи, показывать понятный UX и иногда использовать navigator.storage.persist(), если приложению действительно нужен более устойчивый storage.

Практический вывод

В сильном ответе вы показываете не только определение, но и границы применения. IndexedDB хороша для сложных локальных данных, но требует аккуратного кода вокруг жизненного цикла базы, транзакций, ошибок и синхронизации.

Если хотите звучать практично, добавьте пример из UI. Например, вы сохранили бы в IndexedDB черновики формы или список задач для offline-режима, но после восстановления сети синхронизировали бы их с сервером и обработали конфликты. Такая формулировка показывает, что вы думаете не только про API, но и про состояние продукта.

Частые ошибки

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Называть IndexedDB большим localStorage

    Такая формулировка звучит просто, но технически неверна. IndexedDB работает с object stores, индексами, транзакциями и structured clone, а localStorage хранит строки и выполняет операции синхронно.

  2. 2

    Забывать про миграции схемы

    Object stores и индексы нельзя надежно добавлять где угодно после открытия базы. Схему меняют в onupgradeneeded при повышении версии. Иначе часть пользователей получит ошибку или старую структуру данных.

  3. 3

    Игнорировать ошибки транзакций

    Запись может упасть из-за квоты, дубликата ключа, закрытой базы или приватного режима. Если UI уже показал успех, пользователь потеряет доверие и может потерять черновик.

  4. 4

    Делать IndexedDB единственным источником истины

    Браузерные данные может очистить пользователь, браузер или политика устройства. Для важных данных нужна синхронизация с сервером. IndexedDB лучше описывать как локальный кэш или offline-слой.

  5. 5

    Хранить секреты без модели угроз

    IndexedDB не защищает токены от XSS в вашем приложении. Если скрипт получил доступ к странице, он часто сможет читать и локальное хранилище. Безопасность решают CSP, защита от XSS, cookie strategy и серверные ограничения.

  6. 6

    Вызывать IndexedDB во время render или SSR

    indexedDB есть только в браузере. Если обратиться к нему на сервере или прямо во время render, можно получить ошибку гидратации, зависший экран или лишние чтения при каждом рендере. Безопаснее запускать чтение в effect, обработчике события или отдельном клиентском слое. Еще важно проверять, что результат относится к текущему экрану.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание IndexedDB в реальном frontend-коде.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание