Menu Close

IndexedDB Manipulation with Dexie — Binary Data and Transactions

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.

Storing Binary Data

We can store binary with Dexie.

For example, we can write:

(async () => {
  try {
    const db = new Dexie("friends");
    db.version(1).stores({
      friends: "name"
    });
    const res = await fetch("https://images.pexels.com/photos/614810/pexels-photo-614810.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260");
    const blob = await res.blob();
    await db.friends.put({
      name: "David",
      image: blob
    });
  } catch (error) {
    console.log(error);
  }
})()

We get the image with the fetch

Then we convert it to a blob with the res.blob method.

And then we set it as the value of a field.

Indexing Binary Data

IndexedDB 2.0 supports indexing binary data.

For example, we can write:

(async () => {
  try {
    const db = new Dexie("friends");
    db.version(1).stores({
      friends: "id,name"
    });
    await db.friends.put({
      id: new Uint8Array([1, 2, 3, 4, 5]),
      name: "David"
    });

    const friend = await db.friends.get(
      new Uint8Array([1, 2, 3, 4, 5]));

    console.log(friend.name);
  } catch (error) {
    console.log(error);
  }
})()

to add the id column as one of the index fields.

Then we can set the id field to a binary value with the Uint8Array constructor.

And then we call get to search for the given binary value.

Transactions

We can create a transaction to create atomic commits to our IndexedDB database.

This allows us to roll back easily in case of errors.

For example, we can write:

(async () => {
  try {
    const db = new Dexie("friends");
    db.version(1).stores({
      friends: "id,name,age"
    });
    await db.friends.put({
      id: 1,
      name: "David",
      age: 20
    });

    await db.transaction('rw', [db.friends], async () => {
      const friend = await db.friends.get(1);
      ++friend.age;
      await db.friends.put(friend);
    });

    const someFriend = await db.friends.get(1)
    console.log(someFriend)
  } catch (error) {
    console.log(error);
  }
})()

We add an entry to the friends table.

Then we call db.transaction with the 'rw' flag to let us read and write.

The 2nd argument is the tables we’re applying transactions to.

Then we get an entry from the friends table, increment th age, then save the entry to the database.

Now when we get the same entry, we should see the age is 21.

We can use transactionless code in transactions.

For example, we can write:

const db = new Dexie("friends");

db.version(1).stores({
  friends: "id, name, *tags"
});

function goodFriends() {
  return db.friends
    .where('tags')
    .equals('close-friend');
}

async function addComment(friendId, comment) {
  await db.friends
    .where('id')
    .equals(friendId)
    .modify(friend => {
      friend.comments.push(comment);
    });
}

(async () => {
  try {
    await db.friends.put({
      id: 1,
      name: "jane",
      tags: ['close-friend'],
      comments: []
    });

    await db.friends.put({
      id: 2,
      name: "mary",
      tags: [],
      comments: []
    });

    await db.transaction('rw', db.friends, async () => {
      const goodFriendKeys = await goodFriends().primaryKeys();
      await Promise.all(
        goodFriendKeys.map(id => addComment(id, "I like you!"))
      );
    });

    console.log(db.friends.toArray())
  } catch (error) {
    console.log(error);
  }
})()

We have the goodFriends function that returns a query for the friends table with the close-friend' tag.

The addComment function lets us get the friend by the friendId .

Then we call modify to modify the friend entry by adding a string to the comments field.

In the try block, we add some friends entries.

And then we call db.transaction to create the transaction.

We have the same first 2 arguments. Then we get the primary keys of the friends that found with the primaryKeys method.

And then we call Promise.all to loop through the keys and call the addComment function with it to add a comment to the comments array.

In the console.log , we should see the comments field displayed.

Conclusion

We can store binary data and use transactions to create atomic operations with Dexie.

Posted in JavaScript APIs