Web APIs Cheatsheet
Storage (localStorage, Cookies, IndexedDB)
Use this Web APIs reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
localStorage and sessionStorage
Both implement the
Storageinterface.localStoragepersists across sessions;sessionStorageis scoped to the browser tab.
// Set localStorage.setItem('key', 'value'); localStorage.setItem('obj', JSON.stringify({ a: 1 })); // Get localStorage.getItem('key'); // 'value' or null JSON.parse(localStorage.getItem('obj')); // { a: 1 } // Remove localStorage.removeItem('key'); localStorage.clear(); // remove all // Enumerate localStorage.length; // number of items localStorage.key(0); // key at index for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); console.log(k, localStorage.getItem(k)); } // or Object.entries(localStorage); // all [key, value] pairs
Storage API Quick Reference
| Method / Property | Description |
|---|---|
.setItem(key, value) | Store string value |
.getItem(key) | Retrieve string or null |
.removeItem(key) | Delete entry |
.clear() | Delete all entries |
.key(index) | Get key at numeric index |
.length | Number of stored items |
Storage Event
// Fires on OTHER tabs in the same origin (not the writing tab) window.addEventListener('storage', e => { e.key; // changed key (null if clear() called) e.oldValue; // previous value string e.newValue; // new value string (null if removed) e.url; // URL of the page that changed storage e.storageArea; // localStorage or sessionStorage reference });
Gotchas
- Only strings are stored. Always
JSON.stringify/JSON.parsefor objects.localStorageis synchronous and blocks the main thread on large reads/writes — prefer IndexedDB for bulk data.- Quota is ~5–10 MB per origin. Exceeds throw
DOMException: QuotaExceededError.- Not available in
file://origins or private browsing in some browsers.sessionStorageis NOT shared between tabs, even on the same origin.
IndexedDB
Asynchronous, transactional, key-value/object-store DB. Quota is typically 50%+ of disk. Supports complex queries via indexes.
Open and Upgrade
const request = indexedDB.open('MyDB', 2 /* version */); request.onupgradeneeded = e => { const db = e.target.result; const oldVersion = e.oldVersion; if (oldVersion < 1) { const store = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true }); store.createIndex('by-email', 'email', { unique: true }); store.createIndex('by-name', 'name', { unique: false }); } if (oldVersion < 2) { db.createObjectStore('settings', { keyPath: 'key' }); } }; request.onsuccess = e => { const db = e.target.result; db.onversionchange = () => { db.close(); location.reload(); }; }; request.onerror = e => console.error(e.target.error);
CRUD Operations
// Helper: wrap IDB request in a Promise function idbRequest(req) { return new Promise((res, rej) => { req.onsuccess = () => res(req.result); req.onerror = () => rej(req.error); }); } // Add / Put const tx = db.transaction('users', 'readwrite'); const store = tx.objectStore('users'); const id = await idbRequest(store.add({ name: 'Alice', email: 'a@a.com' })); await idbRequest(store.put({ id, name: 'Alice B', email: 'a@a.com' })); // upsert await tx.done; // only available in idb library; else use tx.oncomplete // Get const tx2 = db.transaction('users', 'readonly'); const user = await idbRequest(tx2.objectStore('users').get(id)); // Get by index const byEmail = await idbRequest( tx2.objectStore('users').index('by-email').get('a@a.com') ); // Delete const tx3 = db.transaction('users', 'readwrite'); await idbRequest(tx3.objectStore('users').delete(id)); // Clear store await idbRequest(tx3.objectStore('users').clear());
Cursors and Range Queries
const tx = db.transaction('users', 'readonly'); const store = tx.objectStore('users'); const index = store.index('by-name'); // IDBKeyRange IDBKeyRange.only('Alice'); IDBKeyRange.lowerBound('A', false); // false = inclusive IDBKeyRange.upperBound('Z', true); // true = exclusive IDBKeyRange.bound('A', 'M', false, true); // Cursor const req = index.openCursor(IDBKeyRange.bound('A', 'M')); req.onsuccess = e => { const cursor = e.target.result; if (!cursor) return; // done console.log(cursor.key, cursor.value); cursor.continue(); // next // cursor.advance(2) — skip 2 // cursor.update({...cursor.value, active: true}) // cursor.delete() }; // getAll (no cursor overhead) const all = await idbRequest(store.getAll()); const range = await idbRequest(store.getAll(IDBKeyRange.lowerBound(10), 50 /* limit */)); const keys = await idbRequest(store.getAllKeys()); const count = await idbRequest(store.count());
Transactions
const tx = db.transaction(['users', 'settings'], 'readwrite'); tx.oncomplete = () => console.log('committed'); tx.onerror = e => console.error(e.target.error); tx.onabort = () => console.log('aborted'); tx.abort(); // manually roll back // Transactions auto-commit when no more requests are queued // Do NOT await non-IDB promises inside a transaction — it will close
idb Library (recommended wrapper)
import { openDB } from 'idb'; // npm install idb const db = await openDB('MyDB', 1, { upgrade(db) { db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true }); }, }); await db.add('notes', { text: 'Hello', created: Date.now() }); const note = await db.get('notes', 1); const all = await db.getAll('notes'); await db.put('notes', { id: 1, text: 'Updated' }); await db.delete('notes', 1); // Transaction shorthand const tx = db.transaction('notes', 'readwrite'); await tx.store.add({ text: 'New' }); await tx.done;
Cache API (Service Worker)
// Open / create cache const cache = await caches.open('v1'); // Add requests await cache.add('/index.html'); await cache.addAll(['/style.css', '/app.js']); // Match const res = await cache.match('/index.html'); // Put (manual) const freshRes = await fetch('/data.json'); await cache.put('/data.json', freshRes.clone()); // Delete await cache.delete('/old.js'); // List all cache names const names = await caches.keys(); // Delete old caches await caches.delete('v0'); // Check if a URL is cached const hit = await caches.match('/style.css');
Storage Quota API
const { usage, quota } = await navigator.storage.estimate(); console.log(`${(usage / quota * 100).toFixed(1)}% used`); // Request persistent storage (won't be evicted automatically) const granted = await navigator.storage.persist(); const isPersisted = await navigator.storage.persisted();