Menu Close

IndexedDB Manipulation with Dexie — Transactions and Versioning

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Transactions

When we do more than one operation to our database in sequence, we would normally use a transaction.

Transactions are only completed when the operation succeeds.

This is to ensure that we catch errors and don’t have any partially completed operations done to ur database.

Any errors will cause the operation to roll back.

We can use it to all the write operations synchronously without the need to wait for it to finish before starting the next one.

For example, we can write:

(async () => {
  const db = new Dexie("FriendsAndPetsDB");
  await db.version(1).stores({
    friends: "++id,name,isCloseFriend",
    pets: "++id,name,kind"
  });
  await db.open();
  db.transaction("rw", db.friends, db.pets, async () => {
    await db.friends.add({
      name: "james",
      isCloseFriend: true
    });
    await db.pets.add({
      name: "mary",
      kind: "dog",
      fur: "long"
    });
  })
})()

to add entries to our friends and pets store simultaneously.

We call db.transaction to start a transaction.

The first argument is the permissions we grant for the transaction.

r is for read, and w is for write.

db.friends and db.pets are the data stores.

The callback has the code that we want to run in the transaction.

Database Versioning

Database versioning is essential when working with IndexedDB.

For example, we can write:

(async () => {
  const db = new Dexie("FriendsDB");
  db.version(1).stores({
    friends: "++id,name"
  });
  db.friends.put({
    name: "james",
    phone: "123456",
    email: "james@edxample.com",
    age: 20
  });
})()

to create version 1 of our FriendsDB with:

const db = new Dexie("FriendsDB");
db.version(1).stores({
  friends: "++id,name"
});

We have the id primary key column, which autoincrements as indicated by the ++ operator.

id is an index on the property name.

We may store other properties as we wish.

If we want to add another column to the index, then we increment the version number by writing:

await db.version(2).stores({friends: "++id,name,age"});

We add the age column to the index and increment the database version to 2.

If we need to change the data architecture, then we need to increment the database version and call the upgrade method.

For example, we write:

(async () => {
  const db = new Dexie("FriendsDB");
  db.version(1).stores({
    friends: "++id,name"
  });
  db.version(2).stores({
    friends: "++id,name,age"
  });
  db.version(3).stores({
    friends: "++id,age,firstName,lastName"
  }).upgrade(tx => {
    return tx.table("friends").toCollection().modify(friend => {
      const [firstName, lastName] = friend.name.split(' ');
      friend.firstName = firstName;
      friend.lastName = lastName;
      delete friend.name;
    });
  });
})()

to upgrade the database version with the version method.

Then to update the database structure, we call the modify method with a callback to update existing data to the new architecture.

Now all the existing data would upgrade for existing users.

Conclusion

Dexie supports transactions so that we only commit complete operations.

Also, we can update the indexes and schema by upgrading the database version.

Posted in JavaScript APIs