API Reference
Classes
Cheat Sheet
Operators & filters
WhereClause
Collection
Addons, Adapters and Derived Work
Derived Work
Typescript
Using Dexie with Typescript
Knowledge Base
Questions and Answers
Quick Reference
Declare Database
NOTE: Don't declare all columns like in SQL. You only declare properties you want to index, that is properties you want to use in a where(...) query.
Schema Syntax
&
Unique
*
Multi-entry index
[A+B]
Compound index
Complete Syntax Documentation
Upgrade
Read more about database versioning
Class Binding
Reference: Table.mapToClass()
Add Items
Reference: Table.add() Table.bulkAdd()
Update Items
Reference: Table.put(), Table.bulkPut(), Table.update(), Collection.modify()
Delete items
Reference: Table.delete(), Table.bulkDelete(), Collection.delete()
Query Items
References: Table.where(), WhereClause, Collection
References: Table.where(), WhereClause, Collection.modify()
Reference: Table.filter()
Read more about compound index
In Dexie 2.0, you could do the above query a little simpler:
Or simply:
Reference: Collection.or()
Retrieve TOP 5 items
References: Table.orderBy(), Collection.reverse(), Collection.limit()
Joining
Storing Binary Data
Indexing Binary Data (IndexedDB 2.0)
IndexedDB 2.0 contains support for indexing binary data. This spec is supported by Chrome and Safari and partially Firefox (Firefox has a bug when using binary primary key, but works well with binary index).
Transaction
Ongoing Transaction
The above code snippet shows that you can reuse "transactionless" code (function goodFriends() and addComment()) but execute it within a transaction (spreadYourLove()).
Reference: Dexie.transaction()
Parent Transaction
The above snippet shows that you can also reuse code that is indeed transaction-aware, but encapsulate several such functions in an overall umbrella-transaction.
NOTE: The code above may look like it could only execute this transaction one-at-a-time, but with thanks to zone technology, this code can work in parallell with other transactions. (Dexie implements its own zone system and is not dependent on zone.js)
Reference: Dexie.transaction()
Working with Asynchronic APIs
Dexie.js is an asynchronic API. In synchronic APIs, errors are normally handled using exceptions. This is very convinient because you code on without doing error checking everywhere and instead catch exceptions on a higher level. Asynchronic APIs normally use success- and error events to signal back when operation complete. Since indexedDB uses a combination of exceptions and error events to notify the caller, it is quite cumbersome to code against it using correct error handling - you need both to do try..catch and request.onerror for each and every request. Dexie.js solves this by working with ECMAScript6 compliant Promises making error handling as easy as it is on a synchronous API with try..catch error handling.
Working with Promises
Promise based APIs (such as Dexie.js) will look more like synchronous APIs than event based APIs, but instead of returning the result, it will return an instance of Promise. ECMAScript6 promises has two methods: then() and catch(). These methods expects a callback to call when the operation succeeds or fails respectively. All asynchronic methods in Dexie returns a Promise instance and this makes the API way more easy to use, as you will see in our examples.
Promise-Specific Data (zone)
Dexie Promises supports a pattern similar to Thread-local storage where it is possible to have static properties that is bound to the executing promise and all it's child-promises. This is similar Angular's zone.js but in an unobtrusive way (no requirement of including any monkey-patching script). Dexie.js and it's transaction API heavily depends on it's transaction zones since it enables code to be aware of the currently executing transaction without having to pass transaction objects around. Promise-Specific Data doc.
Exception Handling
With Dexie, in contrary to indexedDB, there is one single way to catch exceptions - through the Promise.catch() method. Nowhere do you need to do a standard try..catch(). The reason for this is to not enforce the caller to need to think about several ways of error handling. When you work with transactions, you will also get the benefit of being able to catch all errors in one single place - at the end of the transaction, instead of having to catch() every promise of each database operation. Any uncaught error (no matter error events, exception or miss-spelled variable in your code) will abort the ongoing Transaction and trigger its returned Promise to reject, waking up any catch() clause attached to the transaction scope.
All transaction promises should either be caught or returned to its caller.
Catching means Handling!
If you catch a Promise from a database operation within a transaction, it will be considered to be handled and the transaction will not be aborted. This could be a common pitfall when people catch promises within transactions just to log it but expecting the transaction to abort. Solution: re-throw the errors that you don't handle!
Working With Transactions
Whenever you want to do more than a single operation, you simplify your code by using transactions. By working with transactions, you get the following benefits:
If modifying database and any error occur - error event or exception of any kind - then transaction will abort and every modification will be rolled back.
No need to handle promises if you don't like. Everything is encapsulated in the transaction so you can handle that instead.
You may do all write operations synchronically without the need to wait for it to finish before starting the next one. (see the 2nd example code below).
Even read-operations can be done the line after a write operations without waiting for write to finish - still your result will include all modifications. This is possible because all operations are queued when there is a pending write operation going on in current transaction.
Not a single error will just slip away - you catch all in the final catch() method - both error events and ordinary exceptions of any kind.
Here is how you enter a transaction block:
Notes:
'friends' and 'pets' are objectStores registered using Version.stores() method.
"rw"
should be replaced with"r"
if you are just going to do read operations.Also errors from chained database operations within the transaction, or plain exceptions happening in any then() callback of any chained operation will be caught by the transaction's catch() method.
It is possible to prohibit the transaction from being aborted if a failing DB operation is caught explicitly:
When working with transactions, you may query a recent add(), put(), update(), delete() or modify() operation on the next line without waiting for it to finish. The waiting is taken care of by the framework. See the difference below on how that may simplify your code to work with a transaction and not having to call .then() all the time.
Code Example Without Transaction
Same Code Example With Transaction
The sample above shows that there's no need to wait for the add() operations to finish when working within the same transaction.
The Auto-Commit Behavior of IndexedDB Transactions
IndexedDB will commit a transaction as soon as it isn't used within the same task. This means that you MUST NOT call any other async API (at least not wait for it to finish) within a transaction scope. If you do, you will get a TransactionInactiveError thrown at you as soon as you try to continue using the transaction. This cannot be worked around by encapsulating the call with Dexie.Promise since it is a behaviour of IndexedDB.
News in Dexie 2.0.0-beta.6: You can now wait for other async APIs and still hold the transaction active, using Dexie.waitFor()
Nested IndexedDB Transactions
Since version 0.9.8, Dexie supports nested transactions:
The power with nested transactions is that functions that use a transaction can be reused by higher-level code that surrounds all of its calls into a bigger transaction.
See also Dexie.transaction() for more information on how to use transactions.
Last updated