CRUD Operations with IndexedDB - Part 1: Creating and Clearing a Database

gwagner's picture

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 ore 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>
  <!-- Load the IndexedDB Promised library (see https://github.com/jakearchibald/idb) -->
  <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 paramter 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) {  // idbCx is a DB connection
            var tx = idbCx.transaction( tableName, "readwrite");
            var os = tx.objectStore( tableName);
            // Promise.all takes a list of promises and resolves if all of them do
            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) {  // idbCx is a DB connection
            var tx = idbCx.transaction( idbCx.objectStoreNames, "readwrite");
            // Promise.all takes a list of promises and resolves if all of them do
            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:

  1. Check if an IndexedDB database with name "testDB" and an empty "books" table has been created.
  2. Press the button test data..
  3. Then check if the "books" table has been populated with 3 book records.
  4. Press the button database.
  5. Then check if the "books" table has been cleared.

Category: