Getting object store already exists inside onupgradeneeded

Isn't the createObjectStore operating on a new version of the database which is empty?

When you get upgradeneeded the database is in whatever state you left it in before. Since you don't know what versions of your code a user will have visited, you need to look at the event's oldVersion to find out what that was. The typical pattern is something like this:

var rq = indexedDB.open('db', 5);
rq.onupgradeneeded = function(e) {
  var db = rq.result;
  if (e.oldVersion < 1) {
    // do initial schema creation
    db.createObjectStore('users');
  }
  if (e.oldVersion < 2) {
    // do 1->2 upgrade
    var s = db.createObjectStore('better_users');
    s.createIndex('some_index', ...);
    db.deleteObjectStore('users'); // migrating data would be better
  }
  if (e.oldVersion < 3) {
    // do 2->3 upgrade
    rq.transaction.objectStore('better_users').createIndex('index2', ...);
  }
  if (e.oldVersion < 4) {
    // do 3->4 upgrade
    db.createObjectStore('messages', ...);
  }
  if (e.oldVersion < 5) {
    // do 4->5 upgrade
    // ...
  }
}

I am curious why the version number is different in the summary line and when expanded.

That one is subtle... I believe at the point where the 5 was logged the database had started the upgrade. But because an exception was thrown in the upgradeneeded handler the upgrade was aborted, and the version number was rolled back to 4 before the details were logged.


The best way to upgrade the DB is checking if the store name is already there. In this example I'm using https://npmjs.com/idb

openDB('db-name', version, {
  upgrade(db, oldVersion, newVersion, transaction) {
    if(!db.objectStoreNames.contains('messages')) {
      db.createObjectStore('messages', { keyPath: "id", autoIncrement: true })
    }
  }
})

If you need to check if an indexName already exist, you can get the objectStore and check for the indexNames property if it contains the indexName you need.

openDB('db-name', version, {
  upgrade(db, oldVersion, newVersion, transaction) {
    const storeName = transaction.objectStore('storeName')
    if(!storeName.indexNames.contains('indexName')) {
        storeName.createIndex('indexName', 'propertyName', { unique: false });
    }
  }
})

Using indexDB API with indexNames and objectStoreNames to check if something is either there or not makes my code way more reliable and easy to maintain, it is also briefly mentioned on Working with IndexDB Using database versioning