demo · v152
Schema migration
onupgradeneeded is IndexedDB's migration hook — it runs once per version bump inside an atomic transaction. The Chrome 152 SQLite backend makes these upgrades more reliable: if the migration aborts or the browser crashes mid-upgrade, the database rolls back to its prior version cleanly rather than leaving it in a partial state.
Walk through three schema versions: v1 (users store), v2 (add posts + full-name index), v3 (migrate data + remove legacy field). After each upgrade, run the integrity check to confirm all records survived and the schema is correct.
Starting point
v0
No database yet
→
Version 1
v1
users (id, name, email)
→
Version 2
v2
+ posts + fullName index
→
Version 3
v3
migrate name → firstName/lastName
Current schema:
— no stores —
Migration log:
Click "Migrate to v1" to start.
// Schema migration via onupgradeneeded — atomic per version
const request = indexedDB.open('showcase-demo', 3);
request.onupgradeneeded = (event) => {
const db = event.target.result;
const oldVersion = event.oldVersion;
// Each case falls through to run all upgrades from current to target
switch (oldVersion) {
case 0:
// v1: create users store
const users = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true });
users.createIndex('email', 'email', { unique: true });
// fall through
case 1:
// v2: add posts store and fullName index on users
const posts = db.createObjectStore('posts', { keyPath: 'id', autoIncrement: true });
posts.createIndex('authorId', 'authorId');
// Modify existing store: add new index (data stays intact)
const usersStore = event.target.transaction.objectStore('users');
usersStore.createIndex('name', 'name', { unique: false });
// fall through
case 2:
// v3: split 'name' into 'firstName'/'lastName'
// Use the transaction to migrate records in-place
const tx = event.target.transaction;
const store = tx.objectStore('users');
store.openCursor().onsuccess = (e) => {
const cursor = e.target.result;
if (!cursor) return;
const record = cursor.value;
const parts = (record.name || '').split(' ');
record.firstName = parts[0] || '';
record.lastName = parts.slice(1).join(' ') || '';
delete record.name;
cursor.update(record);
cursor.continue();
};
store.deleteIndex('name'); // remove old index
store.createIndex('lastName', 'lastName');
break;
}
};
// Chrome 152 SQLite backend: if onupgradeneeded throws or browser crashes,
// the entire upgrade transaction rolls back — no partial schema states.