Изоляция данных по origin

idb-keyval — это минималистичная библиотека для работы с IndexedDB в браузере, предоставляющая упрощённый API для хранения и извлечения данных по ключу. Основные функции библиотеки включают get, set, del, clear и keys, которые оборачивают низкоуровневые операции IndexedDB в промисы, что позволяет писать асинхронный код в привычном стиле async/await.

Пример базового использования:

import { set, get, del } from 'idb-keyval';

// Сохранение значения
await set('username', 'ivan');

// Получение значения
const username = await get('username'); // 'ivan'

// Удаление значения
await del('username');

Изоляция данных по origin

Каждое веб-приложение в браузере работает в контексте определённого origin (протокол + домен + порт). IndexedDB обеспечивает строгую изоляцию данных между origin, что гарантирует, что данные одного сайта недоступны для другого.

idb-keyval полностью наследует эту модель. Любой вызов функций библиотеки создаёт или использует базу данных, которая локальна для origin. Это означает:

  • Данные, сохранённые на https://example.com, не будут доступны на https://another.com.
  • Даже если имена ключей совпадают, базы данных разных origin хранят эти ключи отдельно.
  • Для поддоменов (sub.example.com) данные из example.com недоступны, так как это считается отдельным origin.

Создание собственной базы данных

По умолчанию idb-keyval использует базу данных keyval-store и объектное хранилище keyval. При необходимости можно создавать свои базы данных, чтобы разделять данные внутри одного origin:

import { Store, get, set } from 'idb-keyval';

// Создание отдельного хранилища
const userStore = new Store('user-db', 'user-store');

// Использование кастомного хранилища
await set('theme', 'dark', userStore);
const theme = await get('theme', userStore); // 'dark'

Использование кастомных хранилищ полезно для:

  • Разделения данных разных модулей приложения
  • Изоляции временных и постоянных данных
  • Облегчения очистки конкретного набора данных без затрагивания других

Работа с ключами и значениями

idb-keyval поддерживает любые сериализуемые объекты, включая:

  • Строки, числа, булевы значения
  • Объекты и массивы
  • Blob, File, Map, Set

Библиотека автоматически сериализует данные при сохранении и десериализует при чтении, избавляя от необходимости ручного JSON.stringify/JSON.parse.

Пример хранения сложного объекта:

await set('settings', { theme: 'dark', notifications: true });
const settings = await get('settings'); // { theme: 'dark', notifications: true }

Методы работы с данными

set(key, value, store?) – сохраняет значение по ключу. get(key, store?) – возвращает значение по ключу или undefined, если ключ не найден. del(key, store?) – удаляет значение по ключу. clear(store?) – очищает все данные из хранилища. keys(store?) – возвращает массив всех ключей.

Использование кастомного хранилища через параметр store позволяет изолировать данные даже внутри одного origin, что повышает безопасность и упрощает организацию данных.

Принципы безопасной изоляции

  • Данные одного origin не пересекаются с другим.
  • Для разных модулей приложения рекомендуется создавать отдельные Store, чтобы избежать случайного перезаписывания ключей.
  • При необходимости временного кэша можно использовать отдельное хранилище, которое затем легко очистить с помощью clear().

Асинхронность и обработка ошибок

Все операции библиотеки возвращают промисы. Это обеспечивает:

  • Возможность использования async/await
  • Обработку ошибок через try/catch

Пример безопасного чтения значения:

try {
    const value = await get('user', userStore);
    if (value) {
        console.log('Пользователь найден:', value);
    } else {
        console.log('Пользователь отсутствует');
    }
} catch (err) {
    console.error('Ошибка при работе с IndexedDB:', err);
}

Асинхронный подход предотвращает блокировку основного потока и гарантирует корректную работу с большими объёмами данных.

Практические рекомендации

  • Создавать отдельные Store для разных категорий данных.
  • Всегда использовать асинхронный подход с обработкой ошибок.
  • Хранить только необходимые данные, чтобы не перегружать IndexedDB.
  • Для временных данных использовать отдельное хранилище с возможностью полной очистки.

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