idb-entity

The idb-entity is an ORM-like Entity manager for the HTML5
IndexedDB. The idb-entity has
Promise-oriented
API and provides an advanced query API (provided by the
indexed-db.es6 library).
Quickstart
Here you will find the basic information on how to use the idb-entity library.
Please check the Wiki and the
indexed-db.es6 library for a more
detailed description and examples.
You can install the idb-entity library into your project using npm:
npm install --save idb-entity
Next you can choose to use either the ES2015 modules (located in es2015/
), or
you may use any transpiler you like (for example Babel or Traceur) to transpile
the ES2015 modules to a module system of your choice.
Connecting to a database
The idb-entity relies on the indexed-db.es6 for database connection and
low-level operations. A quick example is shown below, more detailed description
can be found at the
indexed-db.es6 project's website.
import DBFactory from "indexed-db.es6/es2015/DBFactory"
import EntityManagerFactory from "idb-entity/es2015/EntityManagerFactory"
let entityManagerFactoryPromise = DBFactory.open("my database", {
version: 1,
objectStores: [{
name: "fooBar",
keyPath: null,
autoIncrement: true,
indexes: [{
name: "some index",
keyPath: "id",
unique: false,
multiEntry: true
}]
}]
}).then((database) => {
return new EntityManagerFactory(database)
})
Alternatively, the EntityManagerFactory
also accepts a connection promise:
import DBFactory from "indexed-db.es6/es2015/DBFactory"
import EntityManagerFactory from "idb-entity/es2015/EntityManagerFactory"
let connectionPromise = DBFactory.open("my database", {
...
})
let entityManagerFactory = new EntityManagerFactory(connectionPromise)
Once you are done communicating with the database, you can close the
connection using the close()
method:
entityManagerFactory.close().then(() => {
})
Defining entity types
The entity manager relies on typed entities instead of plain objects and object
store names for practical reasons (this also makes code debugging easier). To
define an entity type, create a new class that extends the AbstractEntity
class and defines the static objectStore
property:
import AbstractEntity from "idb-entity/es2015/AbstractEntity"
export default class FooBar extends AbstractEntity {
static get objectStore() {
return "fooBar"
}
}
The objectStore
property defines the name of the Indexed DB object store the
entity manager will use to store the entities of this type. The object stores
must not be shared among entity types.
Getting an entity manager
The entity manager is used to handle entity manipulation. To get an instance of
the entity manager, use the createEntityManager()
method:
let entityManager = entityManagerFactory.createEntityManager()
Every entity manager instance should be used only for a single operation (for
example reacting to a user action or a message received from the server). Once
the operation at hand has been handled, the entity manager instance should be
discarded.
Entity manager instances should never be used between operations, nor should a
single instance be shared across the whole application - this would most likely
lead to data consistency issues and errors caused by attempting to start
multiple (read-write) transactions on the same entity manager.
Note that the entity manager manages entities in a persistence context only
while a transaction is active, because the data consistency is impossible to
assure outside a transaction. The entity manager's persistence context is
automatically cleared after a transaction is ended.
Fetching entities
The entity manager allows you to fetch entities from an Indexed DB database
without having to explicitly start a new transaction. Single records can be
fetched using the find()
method:
entityManager.find(FooBar , primaryKey).then((entity) => {
})
It is also possible to execute high-level queries on an entity object store:
entityManager.query(
FooBar ,
optionalFilter,
optionalOrderBy,
optionalOffset,
optionalLimit
).then((entities) => {
})
The query API is quite powerful, you can learn more about it at the
indexed-db.es6
wiki.
It is also possible to reload an entity from the database:
entityManager.refresh(entity).then((refreshedEntity) => {
entity === refreshedEntity
})
Saving and deleting single entities
Entities can be saved in the database using the persist()
method:
let entity = new FooBar({
foo: "bar",
created: new Date()
})
entityManager.persist(entity).then((savedEntity) => {
entity === savedEntity
})
To delete a previously created entity, use the remove()
method:
entityManager.remove(FooBar , primaryKey).then(() => {
})
Updating and deleting groups of entities
Groups of entities can be easily modified using the updateQuery()
method:
entityManager.updateQuery(
FooBar ,
optionalFilter,
optionalOrderBy,
optionalOffset,
optionalLimit
)((entity) => {
}).then((updatedEntitiesCount) => {
})
To delete a group of entities, use the deleteQuery()
method:
entityManager.deleteQuery(
FooBar ,
optionalFilter,
optionalOrderBy,
optionalOffset,
optionalLimit
).then((deletedEntitiesCount) => {
})
Updating single records and running transactions
To update a single record, it must be modified within a transaction. The
preferred and safe way to run a transaction is using the runTransaction()
method:
entityManager.runTransaction(() => {
return entityManager.find(FooBar, primaryKey).then((entity) => {
entity.modified = true
})
}).then(() => {
return entityManager.find(FooBar, primaryKey)
}).then((entity) => {
entity.modified
})
So how does this work? The entity manager keeps track of entities used within a
transaction using its persistence context.
When a transaction is to be committed, the entity manager determines which
entities in its persistence context have been modified, and saved the modified
ones into the database.
Likewise, if a transaction is aborted, the entity manager reverts any
modifications done to the entities in its persistence context.
The transaction run using the runTransaction()
method is committed when the
promised returned from the passed-in callback is resolved. The transaction will
be aborted if the returned promise is rejected.
API Documentation
The source code is well documented using JSDoc docblock
comments. Go ahead and
take a look!
Browser support
The browser compatibility is provided by the
indexed-db.es6 library. To see the
current browser compatibility, see the
library documentation.
The current state of this project
There are no current plans for additional features (unless a good case for
adding them is made), but the project accepts bug fixes if new bugs are
discovered.
Contributing
Time is precious, so, if you would like to contribute (bug fixing, test writing
or feature addition), follow these steps:
- fork
- implement (follow the code style)
- pull request
- wait for approval/rejection of the pull request
Filing a bug issue might get me to fix the bug, but filing a feature request
will not as I don't have the time to work on this project full-time. Thank you
for understanding.