Кэширование ответов API

Idb-keyval — это простая обертка над IndexedDB, обеспечивающая удобное хранение пар ключ–значение в браузере. Она идеально подходит для кэширования ответов API, поскольку позволяет сохранять данные локально и извлекать их асинхронно, не блокируя основной поток выполнения.

Для начала необходимо установить библиотеку:

npm install idb-keyval

После установки можно импортировать основные функции:

import { get, set, del, clear, keys } from 'idb-keyval';
  • set(key, value) — сохраняет значение value по ключу key.
  • get(key) — возвращает сохраненное значение по ключу key.
  • del(key) — удаляет значение по ключу key.
  • clear() — очищает все данные.
  • keys() — возвращает массив всех ключей.

Все методы возвращают промисы, что позволяет работать с ними асинхронно и использовать async/await.


Кэширование ответов API

Кэширование API-ответов с помощью Idb-keyval состоит из нескольких ключевых этапов:

  1. Формирование ключа для кэша Ключ должен быть уникальным для каждого запроса. Обычно используется комбинация URL и параметров запроса:
function getCacheKey(url, params) {
    return `${url}?${new URLSearchParams(params).toString()}`;
}
  1. Проверка кэша перед запросом Сначала выполняется проверка наличия данных в IndexedDB, чтобы избежать лишних сетевых запросов:
async function fetchWithCache(url, params) {
    const cacheKey = getCacheKey(url, params);
    const cached = await get(cacheKey);
    if (cached) return cached;

    const response = await fetch(url + '?' + new URLSearchParams(params));
    const data = await response.json();
    await set(cacheKey, data);
    return data;
}
  1. Обновление кэша при изменении данных Для API, где данные могут часто обновляться, важно устанавливать политику устаревания кэша. Например, сохранять дату последнего обновления и проверять её перед возвратом данных:
async function fetchWithCacheAndTTL(url, params, ttl = 3600) {
    const cacheKey = getCacheKey(url, params);
    const cached = await get(cacheKey);

    if (cached && (Date.now() - cached.timestamp < ttl * 1000)) {
        return cached.data;
    }

    const response = await fetch(url + '?' + new URLSearchParams(params));
    const data = await response.json();
    await set(cacheKey, { data, timestamp: Date.now() });
    return data;
}

Организация кэша и управление

Хранение объектов с метаданными Idb-keyval позволяет хранить сложные объекты, включая метаданные:

await set('user_123', {
    data: { name: 'Иван', age: 30 },
    fetchedAt: Date.now(),
    source: 'API'
});

Очистка устаревшего кэша Можно периодически проверять ключи и удалять устаревшие данные:

async function cleanupCache(ttl = 3600) {
    const allKeys = await keys();
    for (const key of allKeys) {
        const entry = await get(key);
        if (entry.timestamp && (Date.now() - entry.timestamp > ttl * 1000)) {
            await del(key);
        }
    }
}

Хранение больших массивов данных Idb-keyval оптимизирован под хранение объектов любых размеров. Для больших коллекций лучше разделять данные на части по ключам, чтобы избежать проблем с производительностью:

await set('posts_page_1', page1Data);
await set('posts_page_2', page2Data);

Продвинутые техники

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

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

const apiCacheStore = new Store('api-cache', 'responses');

await set('user_123', userData, apiCacheStore);
const cachedUser = await get('user_123', apiCacheStore);

Это позволяет изолировать разные типы данных, например кэш API и настройки пользователя, и управлять ими отдельно.

Асинхронная инвалидация кэша Можно запускать очистку старых данных в фоне, не блокируя основной поток:

setTimeout(() => cleanupCache(3600), 0);

Комбинирование с Service Worker Idb-keyval хорошо интегрируется с Service Worker для оффлайн-кэширования: ответы API сохраняются в IndexedDB и используются при отсутствии сети.

self.addEventListener('fetch', async event => {
    const url = new URL(event.request.url);
    if (url.pathname.startsWith('/api/')) {
        const cacheKey = url.toString();
        const cached = await get(cacheKey);
        if (cached) {
            event.respondWith(new Response(JSON.stringify(cached)));
        } else {
            const response = await fetch(event.request);
            const data = await response.clone().json();
            await set(cacheKey, data);
            event.respondWith(response);
        }
    }
});

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

  • Для часто обновляемых данных устанавливать TTL и хранить timestamp.
  • Разделять кэш по типам данных с помощью нескольких хранилищ.
  • Использовать ключи, формируемые на основе URL и параметров запроса, для уникальности.
  • Для больших объемов данных избегать хранения всего в одном ключе — разбивать на страницы или сегменты.
  • Регулярно очищать устаревшие данные, чтобы не перегружать IndexedDB.
  • Сочетать с Service Worker для оффлайн-режима и ускорения повторных запросов.

Idb-keyval предоставляет простой, надежный и асинхронный способ кэширования API-ответов, минимизируя количество сетевых запросов и улучшая производительность веб-приложений.