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.