Skip to content

Opening a Database

Unlike the Promise-based fetch() API, indexedDB.open(name, version) returns an IDBOpenDBRequest — not a Promise. You interact with it by attaching event handler callbacks.

const request = indexedDB.open('my-database', 1);

The two arguments are:

  • name — a string that identifies the database within the origin. Every origin has its own isolated namespace.
  • version — a positive integer. If the database does not yet exist, it is created at this version. If it already exists at a lower version, an upgrade is triggered.

Omitting version defaults to version 1 on creation, or the current version if the database already exists.

Fires when the database is first created or when the requested version is higher than the stored version. This is where you define (or migrate) your schema — create object stores, add indexes, delete old stores.

request.onupgradeneeded = (event) => {
const db = event.target.result; // IDBDatabase
// Create an object store with 'id' as the key path
db.createObjectStore('notes', { keyPath: 'id' });
};

event.target.result (equivalently request.result inside the handler) is the IDBDatabase instance. Any schema changes made here are wrapped in an implicit upgrade transaction.

Fires after onupgradeneeded completes (or immediately, if no upgrade was needed). The database is open and ready for use.

request.onsuccess = (event) => {
const db = event.target.result; // IDBDatabase
console.log('Opened:', db.name, 'at version', db.version);
};

Fires when the open request fails — for example, if the user’s browser is in private mode with storage blocked, or if another tab holds a version-change transaction that was blocked.

request.onerror = (event) => {
console.error('Failed to open DB:', event.target.error);
};

After the database is open, your IDBDatabase instance can receive a versionchange event. This happens when another tab (or the same page after a reload) calls indexedDB.open() with a higher version number.

db.onversionchange = () => {
db.close(); // release the connection so the upgrade can proceed
alert('Database is outdated. Please reload the page.');
};

If you do not close the connection, the upgrade in the other context will be blocked until you do.

Wrapping the callback-based API in a Promise makes it composable with async/await:

function openDatabase(name, version) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { keyPath: 'id' });
}
};
request.onsuccess = (event) => {
const db = event.target.result;
db.onversionchange = () => db.close();
resolve(db);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// Usage
const db = await openDatabase('my-database', 1);
console.log('Ready:', db.name, db.version);

Runnable: open, inspect, close, and clean up

Section titled “Runnable: open, inspect, close, and clean up”
Browser Storage
OptionBenefitCost
The event-based async model of indexedDB.openDoesn’t block the main thread while opening a large databaseYou write three levels of callback (onupgradeneeded, onsuccess, onerror) instead of awaiting directly
Version-based schema migrationGives you explicit control over schema changes via oldVersion/newVersionYou must design the migration logic yourself every time you bump the version, and risk forgetting to handle onblocked
Wrapping open() in a PromiseLets you write linear, much more readable async/await codeHides the event lifecycle details — harder to debug if you forget to handle onblocked or onversionchange
  • Not handling the onblocked event — if another tab still has an old connection open, the version upgrade hangs silently with no error, and the user just sees a stuck page with no clue why.
  • Forgetting to close the connection in onversionchange — if you don’t call db.close() when another tab requests an upgrade, that upgrade stays blocked until this tab is closed or reloaded.
  • Not attaching onerror to the request — when open() fails, for example in a private-browsing mode that blocks storage, the error disappears silently with nothing to debug.
What does indexedDB.open("myDB", 1) return?
In which event handler must you call db.createObjectStore()?
What event fires on your open IDBDatabase when another context requests a higher version of the same database?