@codelake/browserdb
TypeScript icon, indicating that this package has built-in type declarations

0.4.3 • Public • Published

browserdb

browserdb is a wrapper around localStorage to enable document like local storing of objects in the browser. It makes persisting and loading persisted objects inside web apps very easy and convenient.

It should mainly be used in PWAs to store state. It should NOT be used to store large amounts of data as it will not scale efficiently in regard to memory and speed! So be careful how you use it or how a user of your application could use it and possibly dump it with too much data.

Note: The master branch will contain work that is still work-in-progress. So if you want to download a stable version of the library source code please get it from the GitHub release page.

Content

Installation

browserdb can be installed via npm i @codelake/browserdb

Retrieving a database

You can just create it manually, or use the BrowserDbManager in order to reduce memory usage and keep data in your app in sync.

import { BrowserDb, BrowserDbManager } from '@codelake/browserdb';
// or via require and object destructuring
const { BrowserDb, BrowserDbManager } = require('@codelake/browserdb');
// or one by one via require
const BrowserDb = require('@codelake/browserdb').BrowserDb;
const BrowserDbManager = require('@codelake/browserdb').BrowserDbManager;

const db = new BrowserDb('foo');

// or via the BrowserDbManager singleton

const db = BrowserDbManager.getInstance().getDatabase('foo');

In either case you get a valid BrowserDb instance. The only difference is, the instance from the BrowserDbManager's getDatabase(name) method will be kept in memory, so the exact instance can be shared across multiple spots in your code.

It is recommended to use the BrowserDbManager as the corresponding data from the actual localStorage is only read ONCE at instantiation time and not synced afterwards!

Retrieving a collection

The database on its own does not do anything yet. In order to read or write data you have to get a BrowserDbCollection. You can easily do so by calling the getCollection(name) method with the name of the desired collection.

const collection = db.getCollection('bar');

If the collection was never accessed before, it will be instantiated for you. During the instantiation the collection will read the data contained in the localStorage field where the key is the returned value of the collection's collectionName() method.

After the first instantiation, the data inside localStorage won't be read again but only written to!

Meta Programming

By now you've seen some ways to retrieve a BrowserDbCollection so you can persist your data. But it is pretty annoying to always fetch the fitting collection via the BrowserDbManager Singleton. So, if you are building your application with classes in TypeScript (or with babel and the decorators plugin) you can also use decorators to retrieve BrowserDb and BrowserDbCollection instances!

To attach a BrowserDb instance to a class field just use the BrowserDbProp decorator:

import {BrowserDb, BrowserDbProp} from '@codelake/browserdb';

class SomeComponent {
  @BrowserDbProp('foo')
  private db: BrowserDb;
}

In the above example, the decorator will insert a BrowserDb instance into the db property of a newly created SomeComponent instance. That instance is internally fetched via the BrowserDbManager.

But in your case you may need the whole database but only one of its collections. The steps necessary to do so are very similar to the ones to bind a database:

import {BrowserDbCollection, BrowserDbCollectionProp} from '@codelake/browserdb';

class SomeComponent {
  @BrowserDbCollectionProp('foo', 'bar')
  private collection: BrowserDbCollection;
}

In this example the first parameter of BrowserDbCollectionProp corresponds to the database name and the second parameter corresponds to the collection name.

Collection Usage

browserdb aims work very much document like as a system like MongoDB does. So, the first thing you need before you can work on any data is a database. In this case you need an instance of BrowserDb.

Inserting data

You can insert any data you want into a collection like to following code shows:

collection.insertItem(4);
collection.insertItem('foo');
collection.insertItem({some: 'object'});

Selecting data

To select data from a collection you have to provide a predicate, so the collection can determine what data should be returned.

collection.insertItem({ id: 1, name: 'foo' });
collection.insertItem({ id: 2, name: 'bar' });

const matches = collection.selectItems(x => x.id === 1);
const foo = matches[0];
console.log(foo.name); // will print foo

The selectItems(predicate) method will always return an array of items which match the predicate.

Deleting data

Deleting data required a predicate just as the selection of data does.

collection.insertItem({ id: 1, name: 'foo' });
collection.insertItem({ id: 2, name: 'bar' });

const deletions = collection.deleteItems(x => x.id === 1);

The deleteItems(predicate) method returns an array which contains all the items which have been removed from the collection.

Updating data

To update the data inside a collection you have to provide two callbacks: a predicate and an updater.

collection.insertItem({ id: 1, name: 'Hello' });
collection.insertItem({ id: 2, name: 'Word' });

const updated = collection.updateItems(x => x.id === 2, x => {
  x.name = 'World';
  return x;
});

As the code shows, the updater has to return the new/updated item.

If the updater returns undefined the updating mechanism will keep the original object with its updated property/properties. So you can shorten the update call to

collection.updateItems(x => x.id === 2, x => { x.name = 'World'; });

But remember to keep the curly braces around the assignment. If you omit them, the returned value of the assignment (in the example the string 'World') will become the new/updated item!

Clearing a collection

In case you want to delete all the data contained in a collection you have two options:

collection.clear();
// or
collection.deleteItems(() => true);

If your only goal is to clear the collection, the clear() method is recommended, as it uses less memory. But if you want to perform operations on the deleted items, you should use the second method, since it returns all the deleted items.

Note: Be careful which way of collection clearing you use if your application makes use of events as the clear and deleteItems methods trigger different events! See clear event.

Events

It might also be the case that you are interested in the changes that happen to the data of a BrowserDbCollection. Hooking into such events is fairly easy. To do so, you just have to add a listener (callback) to the event you are interested in.

insert event

This event is triggered after an item has been inserted into the collection. The callback will receive the inserted item and the collection in which the item has been inserted.

collection.on('insert', (item: any, col: BrowserDbCollection) => {
  // do stuff
});

update event

This event is triggered after items in the collection have been updated. The callback will receive the array of updated items and the collection in which the items have been updated.

Note: This event is only triggered if an update took place - so, if an item in the collection matched the predicate.

collection.on('update', (items: any[], col: BrowserDbCollection) => {
  // do stuff
});

delete event

This event is triggered after items in the collection have been deleted. The callback will receive the array of deleted items and the collection from which the items have been deleted.

Note: This event is only triggered if a deletion took place - so, if an item in the collection matched the predicate.

collection.on('delete', (items: any[], col: BrowserDbCollection) => {
  // do stuff
});

clear event

This event is triggered after The collection has been cleared. The callback will receive the collection which has been cleared.

Note: This event will only be triggered if the clear method has been called. If you delete all the items of a collection via a call of the deleteItems method with a predicate which matches all items, it will trigger the delete event instead of the clear event.

collection.on('clear', (col: BrowserDbCollection) => {
  // do stuff
});

remove listeners

It may also come in handy to remove listeners as soon as you are not interested in listening to a collection. To remove a listener, you must provide the event it is listening to as well as the callback itself.

const cb = (_, col) => {
  console.log("Something happened in [%s]", col.getName());
};

collection.on('insert', cb);

collection.removeListener('insert', cb);

Some Q&A

  • When does the data of a collection get persisted?
    • Every time you modify the collection via its CRUD methods (except for reading/selecting data) the collection will automatically be persisted as soon as the operation of the collection has finished
  • What happens if I modify a selected item without using the updateItems method?
    • Nothing - more or less

      If you modify items (in the sense of objects) outside the regular updating mechanism it will be updated in the internal data storage/cache of the collection, but the change will not be persisted. So, if you want to make sure all your changed are persisted, use the regular updating mechanism.

Readme

Keywords

Package Sidebar

Install

npm i @codelake/browserdb

Weekly Downloads

8

Version

0.4.3

License

LGPL-3.0

Unpacked Size

51 kB

Total Files

31

Last publish

Collaborators

  • codelake