IndexedDB for Offline Storage¶
IndexedDB is a low-level, browser-native API for client-side storage, ideal for PWAs requiring robust offline capabilities. Unlike localStorage, it supports structured data, complex queries, and large datasets, making it suitable for apps like task managers, note-taking tools, or offline-first dashboards. This section covers schema design, CRUD operations, and synchronization strategies.
Schema Design Best Practices¶
1. Object Stores and Indexes¶
IndexedDB uses object stores (similar to tables) and indexes (for querying). Design your schema with these principles:
- Single object store per entity: Use one store per data type (e.g.,
tasksfor task data). - Indexes for filtering: Add indexes on frequently queried fields (e.g.,
taskStatusordueDate). - Versioning: Use
onupgradeneededto handle schema migrations.
Example schema:
const request = indexedDB.open("MyPWA_DB", 1);
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains("tasks")) {
db.createObjectStore("tasks", { keyPath: "id" });
db.createObjectStore("users", { keyPath: "userId" });
}
};
2. Data Modeling¶
- Normalize data: Avoid redundant fields.
- Use timestamps: Track
lastModifiedfor versioning. - Limit size: Use blob storage for large files (e.g., images) or split data into chunks.
CRUD Implementation¶
1. Adding Data¶
Use add() or put() to insert/update records.
function addTask(task) {
return new Promise((resolve, reject) => {
const db = indexedDB.open("MyPWA_DB");
db.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(["tasks"], "readwrite");
const store = transaction.objectStore("tasks");
const request = store.add(task);
request.onsuccess = () => resolve(task.id);
request.onerror = (e) => reject(e.target.error.name);
};
db.onerror = (e) => reject(e.target.error.name);
});
}
2. Retrieving Data¶
Use indexes for efficient queries.
function getTasksByStatus(status) {
return new Promise((resolve, reject) => {
const db = indexedDB.open("MyPWA_DB");
db.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(["tasks"], "readonly");
const store = transaction.objectStore("tasks");
const index = store.index("status");
const request = index.openCursor(IDBKeyRange.equal(status));
const results = [];
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
results.push(cursor.value);
cursor.continue();
} else {
resolve(results);
}
};
request.onerror = (e) => reject(e.target.error.name);
};
db.onerror = (e) => reject(e.target.error.name);
});
}
3. Deleting/Updating¶
Use delete() or put() with the primary key.
Synchronization Patterns¶
1. Offline-First Workflow¶
- Cache data locally: Use IndexedDB to store user input.
- Sync on connectivity: Use the Background Sync API to queue sync tasks.
Example: Sync when online
if (navigator.onLine) {
syncTasks();
} else {
console.log("No connection; data will sync later.");
}
2. Conflict Resolution¶
- Timestamp-based merging: Use
lastModifiedto prioritize newer data. - Server-side reconciliation: Push local changes to the backend and resolve conflicts.
Advanced Techniques¶
1. Versioning and Migration¶
Use onupgradeneeded to handle schema changes. For example, adding a new index:
db.onupgradeneeded = function(event) {
const db = event.target.result;
const store = db.createObjectStore("tasks", { keyPath: "id" });
store.createIndex("status", "status", { unique: false });
};
2. Libraries for Simplification¶
Use libraries like Dexie.js to abstract IndexedDB complexity:
const db = new Dexie("MyPWA_DB");
db.version(1).stores({
tasks: "++id, status, dueDate"
});
async function addTask(task) {
await db.tasks.add(task);
}
Key takeaways¶
- IndexedDB is essential for structured offline storage in PWAs.
- Schema design should prioritize object stores, indexes, and versioning.
- Synchronization requires offline-first workflows and conflict resolution strategies.
- Libraries like Dexie.js can simplify complex operations.
- Offline-first design ensures reliability even without network connectivity.