← Back to all snippets
JAVASCRIPT

Caching API Responses for Offline Use with IndexedDB

Enhance web application performance and offline capabilities by caching API responses in the browser's IndexedDB, reducing network requests.

const DB_NAME = 'apiCacheDB';
const DB_VERSION = 1;
const STORE_NAME = 'apiResponses';
const CACHE_EXPIRATION_MS = 5 * 60 * 1000; // 5 minutes

function openDb() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(DB_NAME, DB_VERSION);

        request.onupgradeneeded = event => {
            const db = event.target.result;
            if (!db.objectStoreNames.contains(STORE_NAME)) {
                db.createObjectStore(STORE_NAME, { keyPath: 'url' });
            }
        };

        request.onsuccess = event => resolve(event.target.result);
        request.onerror = event => reject('IndexedDB error: ' + event.target.error);
    });
}

async function getCachedResponse(url) {
    const db = await openDb();
    const transaction = db.transaction([STORE_NAME], 'readonly');
    const store = transaction.objectStore(STORE_NAME);
    const request = store.get(url);

    return new Promise((resolve, reject) => {
        request.onsuccess = event => {
            const cachedData = event.target.result;
            if (cachedData && (Date.now() - cachedData.timestamp < CACHE_EXPIRATION_MS)) {
                console.log('Returning cached data for:', url);
                resolve(cachedData.data);
            } else if (cachedData) {
                console.log('Cached data expired for:', url);
                // Optionally, delete expired data proactively
                deleteCachedResponse(url);
                resolve(null);
            } else {
                resolve(null);
            }
        };
        request.onerror = event => reject('Error getting cached data: ' + event.target.error);
    });
}

async function cacheResponse(url, data) {
    const db = await openDb();
    const transaction = db.transaction([STORE_NAME], 'readwrite');
    const store = transaction.objectStore(STORE_NAME);
    const entry = { url, data, timestamp: Date.now() };
    
    return new Promise((resolve, reject) => {
        const request = store.put(entry);
        request.onsuccess = () => resolve();
        request.onerror = event => reject('Error caching data: ' + event.target.error);
    });
}

async function deleteCachedResponse(url) {
    const db = await openDb();
    const transaction = db.transaction([STORE_NAME], 'readwrite');
    const store = transaction.objectStore(STORE_NAME);
    const request = store.delete(url);

    return new Promise((resolve, reject) => {
        request.onsuccess = () => resolve();
        request.onerror = event => reject('Error deleting cached data: ' + event.target.error);
    });
}

async function fetchWithCache(url, options = {}) {
    const cachedData = await getCachedResponse(url);
    if (cachedData) {
        return cachedData;
    }

    console.log('Fetching from network for:', url);
    const response = await fetch(url, options);
    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    await cacheResponse(url, data);
    return data;
}

// Example Usage:
// (async () => {
//     const apiUrl = 'https://jsonplaceholder.typicode.com/posts/1';

//     console.log('First call (should fetch from network and cache):');
//     const post1 = await fetchWithCache(apiUrl);
//     console.log(post1);

//     console.log('
Second call (should return cached data):');
//     const post2 = await fetchWithCache(apiUrl);
//     console.log(post2);

//     // Simulate cache expiration (for testing)
//     // setTimeout(async () => {
//     //     console.log('
After cache expiration (should fetch from network again):');
//     //     const post3 = await fetchWithCache(apiUrl);
//     //     console.log(post3);
//     // }, CACHE_EXPIRATION_MS + 1000);
// })();
How it works: This JavaScript snippet demonstrates how to implement client-side API response caching using IndexedDB, a powerful browser-side NoSQL database. The `fetchWithCache` function first attempts to retrieve data from IndexedDB. If found and not expired, it returns the cached data immediately. Otherwise, it makes a network request using `fetch`, stores the new response along with a timestamp in IndexedDB, and then returns the data. This approach significantly reduces network requests, improves application performance, and enhances offline capabilities by serving previously fetched data even without an active internet connection.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs