@cepharum/indexed-db
v0.2.0
Published
browser-side wrapper for conveniently working with IndexedDBs
Downloads
8
Readme
indexed-db
a wrapper for using browsers' IndexedDB feature more conveniently
License
MIT
Usage
This package basically consists of two classes Database and Store suitable for establishing and object document management in browser.
Database
This class is representing a single IndexedDb database. Such a database can contain one or more object stores.
:::warning
Don't use constructor to gain access on a database. Work with Database.open()
instead.
:::
Database.open( name, [ upgradeFn ] )
This method opens a database selected by its name and promises the manager for accessing it. In second argument a callback may be given for upgrading the existing database on establishing connection. This callback is invoked with related IDBVersionChangeEvent in first argument, the upgrading database instance in second and the covering transaction in mode "versionchange" in third argument.
First argument ist basically suitable for inspecting the change of version. The second one can be used to create new object store. The last one is required to gain access on existing object stores e.g. for upgrading the set of declared indices.
import { Database } from "@cepharum/indexed-db";
Database.open( "default", ( event, db, transaction ) => {
let storeRef;
if ( Database.hasObjectStore( "users", db ) ) {
storeRef = transaction.objectStore( "users" );
} else {
storeRef = db.createObjectStore( "user", { keyPath: "id" } );
}
storeRef.createIndex( "name", "name", { unique: true } );
} );
Database#close()
Promises to disconnect from current database.
Usually you don't need to care for that as you can re-open same database multiple times relying on an internally managed runtime cache to re-use existing database connection.
Database.getDatabaseVersion( name )
This static method enumerates existing databases retrieving version of named one or NaN if it's missing.
As of late 2019 this method works in Chrome, only.
Database.hasObjectStore( name, database )
This static method is provided mostly for use in upgrade handlers, only. Pass database instance provided in second argument there in second argument named database here.
You should stick with the non-static version described below in other use cases. It returns true if named object store is part of given database and false otherwise.
Database#hasObjectStore( name )
This method is testing if current database includes named object store. It returns true if named object store is part of current database and false otherwise.
if ( db.hasObjectStore( "users" ) ) {
// TODO: enable user-related features
}
You can't use approach for conditionally creating or reorganizing object stores.
Database#drop()
Promises to delete current database from browser. This will remove all contained object stores irreversibly.
db.drop()
.then( () => {
// TODO: e.g. re-create database
} );
Database.api
This read-only property is exposing the browser's IndexedDb API.
Store
Object stores in IndexedDB databases are the equivalent of tables in SQL databases or collections in Non-SQL databases. This class is meant to wrap one object store each. It is exposing methods users of SQL and Non-SQL databases might be more familiar with.
:::warning
Don't use constructor to gain access on a store. Work with Store.open()
instead.
:::
Store.open( dbName, storeName, schema )
This static method opens named object store in selected database of current browser. Using an internally managed runtime cache requests for same database and/or store are detected resulting in same instances used to access the resulting store.
The method is promising instance of Store prepared for accessing selected object store.
import { Store } from "@cepharum/indexed-db";
Store.open( "users", "default", {
id: { primary: true },
name: { unique: true },
level: {},
lastLogin: {}
} )
.then( store => {
// TODO: use `store` as demonstrated below
} );
By intention, there is no support for closing a store manager.
Store#drop()
This method promises to remove the current object store from its database.
store.drop()
.then( () => {
// TODO: e.g. re-create store
} );
Store#clear()
This method promises to remove all records from current object store. In opposition to Store#drop() it doesn't remove the store itself.
store.clear()
.then( () => {
// TODO: start fresh
} );
Store#read( id, [ fallback ] )
This method promises to read record from current store selected by its ID based on defined primary key.
store.read( "USER1234" )
.then( record => {
console.log( record );
} );
When there is no matching record in object store optionally provided fallback value is provided instead. When omitting second argument this fallback value is null.
Store#write( id, record )
This method promises to adjust value of named record in current object store.
store.write( "USER1234", { name: "j.doe" } )
.then( store => {
// TODO: keep on processing ...
} );
Store#remove( name )
This method promises to remove named record from current object store.
store.remove( "USER1234" )
.then( store => {
// TODO: keep on processing ...
} );
Store#count( [ range , [ name ] ] )
When invoked without any argument, this method promises total number of records in current store.
store.count()
.then( total => {
console.log( "store %s has %d record(s)", store.name, total );
} );
When providing a range the total number of records with primary key matching that range is returned. By naming a particular property found in schema the range is applied onto that property resulting in number of records matching range with named property.
Store#list( [ options ] )
This method fetches all records in store. It returns a promise for some object containing the list of matching items in property named items. Every such item is another object containing the matching record as well as its key.
store.list()
.then( ( { items } ) => {
for ( let i = 0; i < items.length; i++ ) {
const { key, record } = items[i];
// TODO: process record ...
}
} );
In first argument you may provide an object providing options for customizing retrieval of records from store. These options are available:
| option | default | description |
|--------|---------|-------------|
| offset | 0
| Requests to skip this number of records before collecting for retrieval. |
| limit | Infinity
| Requests to stop collecting records for retrieval after this number of collected records. |
| count | false
| Set this true
to get the total number of (matching) records in property total of promised result right next to items. |
| indexName | null
| Names property to use for searching and/or sorting records.The property must be given in schema provided in call to Store.open(). |
| range | null
| This instance of IDBKeyRange is used to optionally filter records. Any given range is applied to the primary key unless selecting some different property with index in option indexName.
| ascending | null
| This boolean controls whether records should be listed in ascending order with regards to value of primary key or selected property. |
| distinct | null
| This boolean controls whether result is containing every matching record or just one record per matching value of selected property. |
Store#findMatching( name, value, [ options ] )
This method is basically wrapping around Store#list() simplifying its signature for searching records with properties matching given value.
store.findMatching( "level", "guest" )
.then( ( { items } ) => {
// TODO: process records of store with level "guest"
} );
FWIW, this is basically equivalent to the following:
store.list( {
indexName: "level",
range: IDBKeyRange.only( "guest" ),
} )
.then( ( { items } ) => {
// TODO: process records of store with level "guest"
} );
The third arguments supports options offset, limit and count as described in context of Store#list() before.
Store#findInRange( name, lower, [ upper ], [ options ] )
Just like Store#findMatching(), this method is wrapping around Store#list() for simplifying code. It is promising all records with values of named property matching range defined by arguments lower and upper.
When providing null
for either boundary the range isn't limited accordingly.
store.findInRange( "lastLogin", new Date( "2019-01-01 12:00:00Z" ) )
.then( ( { items } ) => {
// TODO: process records users that have logged in since 2019
} );
FWIW, this is basically equivalent to the following:
store.list( {
indexName: "level",
range: IDBKeyRange.only( "guest" ),
} )
.then( ( { items } ) => {
// TODO: process records of store with level "guest"
} );
The third arguments supports options offset, limit, count, ascending and distinct as described in context of Store#list() before.