JAVASCRIPT
Client-Side API Response Caching with IndexedDB
Enhance web application performance and offline capabilities by caching API responses securely and persistently using the browser's IndexedDB.
const DB_NAME = 'apiCacheDB';
const DB_VERSION = 1;
const STORE_NAME = 'apiResponses';
let db;
async function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = (event) => {
console.error('IndexedDB error:', event.target.errorCode);
reject('IndexedDB error');
};
request.onupgradeneeded = (event) => {
db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'url' });
objectStore.createIndex('timestamp', 'timestamp', { unique: false });
}
};
request.onsuccess = (event) => {
db = event.target.result;
resolve(db);
};
});
}
async function getCachedResponse(url, maxAgeMs = 3600000) { // 1 hour default cache
if (!db) await openDatabase();
return new Promise((resolve) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.get(url);
request.onsuccess = (event) => {
const cachedData = event.target.result;
if (cachedData && (Date.now() - cachedData.timestamp < maxAgeMs)) {
console.log('Cache hit for:', url);
resolve(cachedData.data);
} else {
console.log('Cache miss or expired for:', url);
resolve(null); // Cache miss or expired
}
};
request.onerror = (event) => {
console.error('Error getting cached data:', event.target.errorCode);
resolve(null);
};
});
}
async function setCachedResponse(url, data) {
if (!db) await openDatabase();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.put({ url, data, timestamp: Date.now() });
request.onsuccess = () => {
console.log('Data cached for:', url);
resolve();
};
request.onerror = (event) => {
console.error('Error setting cached data:', event.target.errorCode);
reject('Error setting cached data');
};
});
}
async function fetchAndCache(url, options = {}, maxAgeMs = 3600000) {
const cached = await getCachedResponse(url, maxAgeMs);
if (cached) {
return cached;
}
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
await setCachedResponse(url, data);
return data;
}
// Example usage:
// fetchAndCache('https://api.example.com/products', { headers: { 'Accept': 'application/json' } }, 60 * 60 * 1000) // Cache for 1 hour
// .then(data => console.log('Fetched (potentially cached) products:', data))
// .catch(error => console.error('Error:', error));
How it works: This JavaScript snippet provides a client-side caching mechanism for API responses using `IndexedDB`. It wraps the standard `fetch` API, first attempting to retrieve data from a dedicated IndexedDB object store. If a valid, non-expired cache entry is found, it returns the cached data instantly. Otherwise, it proceeds with a network request, caches the fresh response along with a timestamp, and then returns it. This approach significantly improves performance, reduces network requests, and enhances the application's offline capabilities by storing larger datasets persistently than `localStorage`.