This blog post has been updated (on 07/12/2021) by using async/await, see the updated post on web-engineering.info.
In this article, we show how to create and clear an IndexedDB
database keeping all code in a single HTML file. Creating an IndexedDB
database means first creating an empty database, and then adding records to its empty tables (or "object stores"). In Part 2, we will explain how to retrieve, update and delete IndexedDB
records, such that we get the full collection of CRUD operations: Create
, Update
, Retrieve
and Delete
.
This is not an introduction to IndexedDB
, but rather a guide how to use it for CRUD operations in a front-end app. We assume that the reader has already done an introductory tutorial such as Working with IndexedDB. Recall that an IndexedDB
database is a set of object tables (or "object stores") where each table row, or record, has a standard ID (or "primary key") property defined with keyPath
when creating the database.
As recommended in Working with IndexedDB, we use the IndexedDB Promised library, which wraps the ES5 indexedDB
API with ES6 promise-based methods for obtaining more readable and maintainable code in our IndexedDB
access methods. Download it into a lib folder such that it can be included with <script src="lib/idb.js"></script>
.
We use an object variable IDB
as a namespace
container for three database management methods:
IDB.createEmptyDB(tableNames)
- Checks if there is already a database with the given name, and, if not, creates one with an empty table (or "object store") for each of the table names provided.
IDB.add(tableName, records)
- Adds one or more records to the given table.
IDB.clearDB()
- Clears the contents of all tables.
We start with looking at the code of the createEmptyDB
method:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="UTF-8" />
<title>CRUD Operations with IndexedDB - Part 1</title>
<!--
<script src="lib/idb.js"></script>
<script>
var IDB = {
dbName: "testDB",
createEmptyDB: function ( tableNames) {
return new Promise( function (resolve) {
idb.open( IDB.dbName, 1, function (upgradeDb) {
tableNames.forEach( function (tableName) {
if (!upgradeDb.objectStoreNames.contains( tableName)) {
upgradeDb.createObjectStore( tableName, {keyPath:"id"});
}
})
}).then( resolve);
});
},
We define IDB.dbName
as the name of our IndexedDB
database. The IDB.createEmptyDB
method takes a list of table names and tries to open the database (with version number 1). If this fails, that is, if the database does not yet exist, the function with the parameter upgradeDb
will be called, taking care of creating an empty object store with a standard ID property "id
" for each of the table names provided.
For adding a list of records to an object table/store, we invoke the IndexedDB
add
method on the object store for each of them. Since each add
invocation returns a promise, we create a list of promises by mapping the records
array to an array of add
invocation return values and use the Promise.all
method for resolving this list only when all of them have resolved:
add: function ( tableName, records) {
return new Promise( function (resolve, reject) {
idb.open( IDB.dbName).then( function (idbCx) {
var tx = idbCx.transaction( tableName, "readwrite");
var os = tx.objectStore( tableName);
return Promise.all( records.map( rec => {return os.add( rec);}))
.then( function () {return tx.complete;});
}).then( resolve)
.catch( function (err) {
reject( err);
});
});
},
For clearing the entire database contents, we need to clear all object tables/stores by invoking the IndexedDB
clear
method on them. Again, we create a list of promises by mapping the idbCx.objectStoreNames
collection to an array of clear
invocation return values and use the Promise.all
method for resolving this list only when all of them have resolved:
clearDB: function () {
return new Promise( function (resolve) {
idb.open( IDB.dbName).then( function (idbCx) {
var tx = idbCx.transaction( idbCx.objectStoreNames, "readwrite");
return Promise.all( Array.from( idbCx.objectStoreNames,
osName => {return tx.objectStore( osName).clear();}))
.then( function () {return tx.complete;});
}).catch( function (err) {
console.log( err);
}).then( resolve);
});
}
}
Finally, we define a createTestData
method and HTML buttons for invoking it and for invoking the clear method:
function createTestData () {
IDB.add( "books", [
{id: "006251587X", title: "Weaving the Web", year: 2000, edition: 2},
{id: "0465026567", title: "Gödel, Escher, Bach", year: 1999},
{id: "0465030793", title: "I Am a Strange Loop", year: 2008}
]);
}
</script>
</head>
<body>
<h1>Working with IndexedDB</h1>
<h2>Creating and Clearing a Database</h2>
<ul>
<li><button type="button" onclick="IDB.createEmptyDB(['books']).then
( createTestData())">Generate</button> test data</li>
<li><button type="button"
onclick="IDB.clearDB()">Clear</button> database</li>
</ul>
</body>
</html>
The createTestData
method is invoked with the following JS expression as the value of the button's onclick
event handler attribute:
IDB.createEmptyDB(['books']).then( createTestData())
Now it's time to try this out. Copy and append all 4 HTML/CSS code fragments from this article in a text editor and save it as an HTML file. Then, open it in a browser that allows accessing IndexedDB
databases also via file:
URLs, such as Chrome (unfortunately, Firefox doesn't allow this). Otherwise, open it via a local web server (e.g., using NodeJS). You can inspect the existence and contents of IndexedDB
databases with the help your browser's developer tools (e.g., with Shift-Ctrl-I). Try out the following steps:
- Check if an
IndexedDB
database with name "testDB
" and an empty "books
" table has been created. - Press the button Generate test data.
- Then check if the "
books
" table has been populated with 3 book
records. - Press the button Clear database.
- Then check if the "
books
" table has been cleared.