Lightweight promise-based wrapper for fast & simple access to IndexedDB API. With React integration βοΈ
To start using IDB.js install it from npm
npm i @lr0pb/idb
Provide name, version and stores to IDB constructor
import { IDB } from '@lr0pb/idb';
const db = new IDB('library', 1, [
{ name: 'books', index: { keyPath: 'id' } },
{ name: 'authors', index: { keyPath: 'name' } },
{ name: 'manufacturers', index: { autoIncrement: true } }
]);stores is an array of StoreDefinition objects: name of store and index object, which described how should be indexed data inside the store. This is a IDBObjectStoreParameters object, which is a part of original IndexedDB API
You can also provide optional fourth argument options described in new IDB
You can add or delete stores from database by called IDB constructor with upgraded version and relevant changes in stores array
-const db = new IDB('library', 1, [
+const db = new IDB('library', 2, [
{ name: 'books', index: { keyPath: 'id' } },
{ name: 'authors', index: { keyPath: 'name' } },
- { name: 'manufacturers', index: { autoIncrement: true } }
+ { name: 'visitors', index: { keyPath: 'id' } }
]);
// 'manufacturers' store will be deleted and cannot be longer accessed
// 'visitors' store instead will be created in databaseYou can delete and add as much stores in a single database update as you want
Operate with data:
Other helpful methods:
IDB come out-of-the-box with types declaration.
While using IDB in TS project, every data related method (exludes deleteAll) have a type parameters, where T stands for Type of data you operate and K stands for Key to access this data in the store.
You can learn detailed types annotation for the concrete method by clicking [Ref] link in the method description within this Readme or you can explore all types stuff on docs site
[Ref] Add item to the store via db.set(store, item | items[]) method
await db.set('authors', {
name: 'Agatha Christie',
books: [...]
});
await db.set('authors', [
author1, author2, ...
]);[Ref] Get one or more items by keys with db.get(store, key | keys[]) method
const author = await db.get('author', 'Agatha Christie');
// {
// name: 'Agatha Christie',
// books: [12345, 67890, ...],
// ...
// }
const authorsBooks = await db.get('books', author.books);
// [
// {
// id: 12345,
// title: `Hercule Poirot's Christmas`,
// ...
// },
// {
// id: 67890,
// title: `Murder on the Orient Express`,
// ...
// },
// ...
// ][Ref] Read all items in the store with db.getAll(store, DataReceivingCallback?) method
const books = await db.getAll('books');
books.forEach((book) => {
renderBook(book);
});Additionally, you can set DataReceivingCallback that will be called every time new item receives from the database
await db.getAll('books', (book) => {
renderBook(book);
});
DataReceivingCallbackfunction must be sync
[Ref] Use db.update(store, key | keys[], UpdateCallback) to easier updating items in the store. It is actually simple wrapper for get and set methods
async function addBookToAuthor(book) {
await db.update('authors', book.author, async (author) => {
// this callback function receives item object, to update it,
// you should apply changes directly to this object
author.books.push(book.id);
await sendAnalytics();
});
}
UpdateCallbackfunction can be async If you provide multiple keys,UpdateCallbackwill be called for each received item. If you want to use separateUpdateCallbackfunctions for each item, provide array ofUpdateCallbackfunctions same length askeysarray length
[Ref] Delete one or more items by keys with db.delete(store, key | keys[]) method and clear all store entries with db.deleteAll(store) method [Ref]
await db.delete('books', 12345);
await db.delete('books', [
67890, 34567, ...
]);
await db.deleteAll('author');
// `authors` store is still available but have zero items[Ref] Check if store have certain items via db.has(store, key | keys[] | void) or get amount of all items in the store by not passing key argument
const book = {
id: 12345,
title: `Hercule Poirot's Christmas`,
...
};
await db.set('books', book);
const isBookSaved = await db.has('books', book.id); // true
const booksCount = await db.has('books'); // 1[Ref] You can register multiple listeners to spot changes that happened in the store with db.onDataUpdate(store, StoreUpdatesListener) method. These callbacks will be called after actual operation with data in order to time they are registered
To unregister callback, call returned from db.onDataUpdate UnregisterListener function
const unregister = await db.onDataUpdate('books', async ({store, type, item}) => {
// `item` argument not presented when it was a deleting operation
if (type === 'set' && isNewBook(item)) {
await notifyUserAboutNewBookAdded(item);
}
});
...
unregister();
StoreUpdatesListenerfunction can be async
View whole detailed API documentation with all the types and overloads on docs site
- 2.1.0 Explicitly throw errors when something goes wrong in IDB methods call
View all changes during versions in CHANGELOG
As this project initially was a data layer part of my another project, I maintain it preferably for those project, but any issues and pull requests are welcome!
For start to develop IDB: clone this repo on your machine and run npm i
Write your code and create tests for it in test directory. Each .test.js file should be structered as shown below:
import { assert } from 'chai/index.mjs';
export default function (container, checkStore) {
it('test', ...);
...
}Every call to IDB should be accessed through container.db variable. checkStore(store, count) is helper function to check amount of items in store.
After new test file created, it should be imported inside test/__index__.test.js file and passed to tests object to automatically run with other tests. To disable some test suite - comment its desclaration within tests object.
Run tests via npm test - it will start a development server and open default browser window with tests page