idb: A functional, promise-based wrapper for IndexedDB

IndexedDB is a capable no-sql object database that is now available in recent versions of all major browers, but it is accessed via an unusual and unfamiliar request based API which can be difficult to use at first, and which doesn't utilize promises or async/await function calls.

The idb library is a small open-source library which aims to make use of an IndexedDB object store a bit simpler. The library is functional and promise based; functional because its API is provided as a set of functions rather than methods on a DB object; and promise based as all of its functions return promises, and can be called using async/await.

The project's README includes a full description of the API and sample code.

Transactions and async code

One of the trickier parts of the IndexedDB API is learning how transactions work, particularly when used within asynchronous code.

A transaction is started when one an IndexedDB's object stores is opened, and is automatically closed at the end of a process tick if the transaction has no pending operations.

    let objStore = idbOpenObjStore( schema, 'files');

    // This call is ok; the transaction has just started.
    let file1 = await idbRead( objStore, 'file1');

    // This call will fail; the transaction was closed when the previous
    // call returned, and no other operations in the transaction were
    // pending.
    let file2 = await idbRead( objStore, 'file2');

There are various ways to get around this problem. One is to try and perform all operations on the object store in the same process tick:

    // This will work, as both idbRead calls take place in the same process
    // tick:
    let [
        file1,
        file2
    ] = await Promise.all([
        idbRead( objStore, 'file1'),
        idbRead( objStore, 'file2')
    ]);

The other is simply to start a new transaction at the start of the next process tick:

    let objStore = idbOpenObjStore( schema, 'files');
    let file1 = await idbRead( objStore, 'file1');

    // Start of a new process tick after the preceeding await.
    objStore = idbOpenObjStore( schema, 'files');
    let file2 = await idbRead( objStore, 'file2');