Skip to content

16 PWA - IndexedDB

IndexedDB

  • IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs.
  • IndexedDB is a large scale NoSQL database.
  • IndexedDB data is stored as key-value pairs in object stores.
  • Supports transactions.

IndexedDB API is not really programmer friendly, use some wrapper library

idb – Jake Archibald’s IndexedDB Promised (idb) is most widely used thin promises based library (written in TS).
https://github.com/jakearchibald/idb

PWA Data

Where should data be stored in PWA app?

  • URL addressable resources – Cache
  • Everything else – IndexedDB

IndexedDB is accessible everywhere – in window, in service worker and in web worker

Terminology

  • Database – cotains object stores, which contain data. Typically one DB per app.
  • Object store – similar to tables in classical DB. Data is stores in json (no real types)
  • Index – Store used to organize data in another store (reference object store)
  • Transaction – same as in regular SQL. Mandatory in IndexedDB for read/write (multithread safety).
  • Cursor – Mechanism to iterate over multiple records in DB.

idb

Define DB Schema

Every property is object store

  • Value: type stored
  • Key: key type
  • Indexes: index name and data value type
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
interface IListDB extends DBSchema {
    items: {
        value: {
            id: string;
            description: string;
            completed: boolean;
        };
        key: string;
        indexes: { 'by-completed': string };
    };
}

idb - open, migrate

Open and upgarde db (migrate data as needed)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const db = await openDB<IListDB>('list-db', VERSION, {
    upgrade(db) {
        if (db.objectStoreNames.contains('items')) {
            db.deleteObjectStore('items');
        }
        const productStore = db.createObjectStore('items', {
            keyPath: 'id',
        });
        productStore.createIndex('by-completed', 'description');
        console.log("DB upgraded!");
    },
});

idb - save

Save data to db

1
2
3
data.forEach(async item => {
    await db.put('items', item);
});

idb - read/write

Normally read/write operations are wrapped into transaction

1
2
3
4
5
const tx = db.transaction('items', 'readwrite');
const store = tx.objectStore('items');
const val = (await store.get('some guid'));
await store.put({...val!, completed: true});
await tx.done;

NB! Do not await other thing inside transaction!

Single commands

  • Its common to use single commands inside transactions, so shortcuts are provided by idb library
  • get, getKey, getAll, getAllKeys, count, put, add, delete, clear
  • getFromIndex, getKeyFromIndex, getAllFromIndex, getAllKeysFromIndex, countFromIndex.

imports in SW

  • ES Modules in SW
  • https://chromium.googlesource.com/chromium/src/+/refs/heads/main/content/browser/service_worker/es_modules.md
1
2
3
4
try {
    const registration = await navigator.serviceWorker
        .register('./sw.js', {type: 'module'});
    console.log('Serwice worker registered!', registration);

Copy libs to sw source folder, import regulary