node package manager

@hoodie/store-server-api

API to manage databases, access and replications, backed by PouchDB

hoodie-store-server-api

API to manage databases, access and replications, backed by PouchDB

Build Status Coverage Status Dependency Status devDependency Status

@hoodie/store-server-api is a JavaScript API to manage databases, access as well as persisted continuous replications. All data is persisted using PouchDB and hence compatible with CouchDB as well as other PouchDB adapters.

Example

var PouchDB = require('pouchdb')
var Store = require('@hoodie/store-server-api')(PouchDB)
 
Store.open('mydb')
 
.then(function (store) {
  store.findAll().then(function (docs) {})
})

API

Factory

var StoreFactory = require('@hoodie/store-server-api')
var PouchDB = require('pouchdb')
var Store = StoreFactory(PouchDB)
Argument Type Description Required
PouchDB PouchDB Constructor PouchDB constructor as returned by require('pouchdb'). Note that you can set defaults to a PouchDB constructor using PouchDB.defaults(options) Yes

Returns Store.* API. See below

Store.open()

Store.open(name)
Argument Type Description Required
name String Name of database. Based on PouchDB’s configuration, databases will be persisted in CouchDB, using LevelDB, in memory or using a custom adapter. Yes

Resolves with Store instance.

Rejects with

Missing Error 404 Store does not exist
Store.open('mydb')
  .then(function (store) {
    // use Hoodie’s store API 
  })
  .catch(function (error) {
    if (error.status === 404) {
      console.log('Store "mydb" does not exist')
    } else {
      console.log('Something unexpected happened:', error)
    }
  })

Store.exists()

Store.exists(name)
Argument Type Description Required
name String Name of database. Yes

Resolves with true / false depending on whether database exists or not.

Example

Store.exists('foo')
  .then(function (doesExist) {
    if (doesExist) {
      console.log('Database "foo" exists');
    } else {
      console.log('Database "foo" does not exists');
    }
  })
  .catch(function (error) {
    console.log('Something unexpected happened:', error)
  })

Store.create()

Creates a database

Store.create(name, options)
Argument Type Description Required
name String Name of database. Yes
options.access String, Array Can be 'read', 'write' or ['read', 'write']. No
options.role String, Array Give access to one or multiple roles. Can only be passed if options.access is set. No

Resolves with name.

Rejects with:

Conflict Error 409 Store already exist

Example

Store.create('mydb')
  .then(function (dbName) {
    console.log('Database %s created', dbName)
  })
  .catch(function (error) {
    if (error.status === 409) {
      console.log('Database %s already exists', dbName)
    } else {
      console.log('Something unexpected happened:', error)
    }
  })

Store.destroy()

Deletes a database

Store.destroy(name)
Argument Type Description Required
name String Name of database. Yes

Resolves with name.

Rejects with:

Missing Error 404 Store does not exist

Example

Store.destroy('mydb')
  .then(function (dbName) {
    console.log('Store "mydb" has been destroyed')
  })
  .catch(function (error) {
    if (error.status === 404) {
      console.log('Store "mydb" does not exist')
    } else {
      console.log('Something unexpected happened:', error)
    }
  })

Store.grant()

Store.grant(dbName, options)

Every store is private by default. Read and write access must be granted explicitly. A store can be set to public read / write, or read / write access can be granted to specific roles only.

Behaviors in detail:

  • Once access was granted to a role it can no longer be public read / write.
  • Once access was granted to 'public', all other roles will be removed
  • Granting access to an existing role has no effect
Argument Type Description Required
name String Name of database. Yes
options.access String, Array Can be 'read', 'write' or ['read', 'write']. Yes
options.role String, Array Give access to one or multiple roles. No

Resolves without arguments. Rejects with

Missing Error 404 Store does not exist

Example

Store.grant('foo', {
  access: 'read'
})
  .then(function () {
    console.log('"foo" can now be read by everyone');
  })
  .catch(function (error) {
    console.log('Something unexpected happened:', error)
  })

Store.revoke()

Store.revoke(dbName, options)

Revoke read / write access entirely or from one / multiple roles.

Argument Type Description Required
name String Name of database. Yes
options.access String, Array Can be 'read', 'write' or ['read', 'write']. Yes
options.role String, Array Revoke access from one or multiple roles. No

Resolves without arguments. Rejects with

Missing Error 404 Store does not exist

Example

Store.revoke('foo', {
  access: 'read'
})
  .then(function () {
    console.log('"foo" can no longer be read by anyone');
  })
  .catch(function (error) {
    console.log('Something unexpected happened:', error)
  })

Store.hasAccess()

Store.hasAccess(dbName, options)

Checks if the given role has access to given database.

Argument Type Description Required
name String Name of database. Yes
options.access String, Array Can be 'read', 'write' or ['read', 'write']. If array passed, checks for access for both. Yes
options.role String, Array If Array passed, checks if any of the roles has access No

Resolves with true / false depending on whether database exists or not.

Example

Store.hasAccess('foo', {
  access: 'read'
})
  .then(function (hasAccess) {
    if (hasAccess) {
      console.log('"foo" can be read by everyone');
    } else {
      console.log('"foo" cannot be read by everyone');
    }
  })
  .catch(function (error) {
    console.log('Something unexpected happened:', error)
  })

Store.replicate

Important: Make sure you have the pouchdb-replication package installed

Store.replicate(source, target, options)

Options are the same as PouchDB.replication. The only difference is that replications with {live: true} will be persisted and resumed. Because of that, Store.replicate does not return a replication instance, but a Promise which resolves with a replication instance.

Store.cancelReplication

Store.cancelReplication(source, target)

Cancels a live replication removes it from the store so it will no longer be resumed. Returns a promise.

Instance

A Store instance has the Hoodie Store API (as returned by db.hoodieApi()). To get a Store instance call Store.open

// all methods return promises 
store.add(object)
store.add([object1, id2])
store.find(id)
store.find(object) // with id property 
store.findOrAdd(id, object)
store.findOrAdd(object)
store.findOrAdd([object1, id2])
store.findAll()
store.findAll(filterFunction)
store.update(id, changedProperties)
store.update(id, updateFunction)
store.update(object)
store.update([object1, id2])
store.updateOrAdd(id, object)
store.updateOrAdd(object)
store.updateOrAdd([object1, id2])
store.updateAll(changedProperties)
store.updateAll(updateFunction)
store.remove(id)
store.remove(object)
store.remove([object1, id2])
store.removeAll()
store.removeAll(filterFunction)
store.clear()
 
// events 
store.on('add', function(object, options) {})
store.on('update', function(object, options) {})
store.on('remove', function(object, options) {})
store.on('change', function(eventName, object, options) {})
store.on('clear', function() {})
store.one(eventName, eventHandlerFunction)
store.off(eventName, eventHandlerFunction)
 
// original PouchDB (http://pouchdb.com/api.html) instance used for the store 
store.db

Events

Event Description Arguments
create New store created successfully name
destroy Existing store destroyed name

How things work

PouchDB only implements APIs on a database level without any kind of access control. In order to keep track of all databases, access settings and replications, we create a meta database hoodie-store. Every database that gets created using Store.create() will also create a document in hoodie-store with _id being db_<db name>. All continuous replications are stored with asreplication_<source db name>_<target db name>.

The database documents have an access property which we use for access control on database level.

The replication documents are stored with source, target and options properties. On server start continuous replications are started for all replication documents.

Note on CouchDB

CouchDB comes with its own REST API, so if it’s accessible at a public URL people can directly access it. For that reason we set /_security on each database created by Hoodie so that it’s only readable by CouchDB admins.

License

Apache 2.0