Submitted by gwagner on
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:
- Check if an IndexedDB database with name "testDB" and an empty "books" table has been created.
- Press the button test data..
- Then check if the "books" table has been populated with 3 book records.
- Press the button database.
- Then check if the "books" table has been cleared.