This document defines APIs for a database of records holding simple values and hierarchical objects. Each record consists of a key and some value. Moreover, the database maintains indexes over records it stores. An application developer directly uses an API to locate records either by their key or by using an index. A query language can be layered on this API. An indexed database can be implemented using a persistent B-tree data structure.

A Candidate Recommendation (CR) of the specification was published on 4 July 2013. Changes to this specification since the CR was published are summarized and enumerated in this document's Revision History appendix.

This specification's bugs and issues are managed in Bugzilla (new bug, open bugs). Feature requests for the next version of this specification are kept in the Indexed Database Features document.

Introduction

User agents need to store large numbers of objects locally in order to satisfy off-line data requirements of Web applications. [[WEBSTORAGE]] is useful for storing pairs of keys and their corresponding values. However, it does not provide in-order retrieval of keys, efficient searching over values, or storage of duplicate values for a key.

This specification provides a concrete API to perform advanced key-value data management that is at the heart of most sophisticated query processors. It does so by using transactional databases to store keys and their corresponding values (one or more per key), and providing a means of traversing keys in a deterministic order. This is often implemented through the use of persistent B-tree data structures that are considered efficient for insertion and deletion as well as in-order traversal of very large numbers of data records.

This specification defines one class of products:

Conforming user agent

A user agent MUST behave as described in this specification in order to be considered conformant.

User agents MAY implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.

A conforming Indexed Database API user agent MUST also be a conforming implementation of the IDL fragments of this specification, as described in the “Web IDL” specification. [[!WEBIDL]]

This specification uses both the terms "conforming user agent(s)" and "user agent(s)" to refer to this product class.

Dependencies

This specification relies on several other underlying specifications.

DOM-LEVEL-3-EVENTS
The terms default action and propagation path are defined by the Document Object Model (DOM) Level 3 Events Specification [[!DOM-LEVEL-3-EVENTS]].
DOM4
The types DOMException and Event are defined by the W3C DOM4 Specification [[!DOM4]].
HTML5
The terms and algorithms document base URL, event handler attributes, event handler event type, Function, origin, same origin, structured clone, structured clone algorithm, task, task source, and queue a task are defined by the HTML 5 specification [[!HTML5]].
WebIDL
Many of the interface definitions and all of the IDL in this spec depends on [[!WEBIDL]].
WebWorkers
The terms Worker and WorkerUtils are defined by the WebWorkers specification [[!WEBWORKERS]].

Indexed Database API

Constructs

Database

A database's origin is the same as the origin of the document or worker. Each origin has an associated set of databases.

The database origin is not affected by changes to document.domain.

Each origin has an associated set of databases. A database comprises one or more object stores which hold the data stored in the database.

Every database has a name which identifies it within a specific origin. The name can be any string value, including the empty string, and stays constant for the lifetime of the database. Each database also has a current version. When a database is first created, its version is 0.

Implementations MUST support all names. If an implementation uses a storage mechanism which can't handle arbitrary database names, the implementation must use an escaping mechanism or something similar to map the provided name to a name that it can handle.

Each database has one version at a time; a database can't exist in multiple versions at once. The only way to change the version is using a "versionchange" transaction.

Databases has a delete pending flag which is used during deletion. When a database is requested to be deleted the flag is set to true and all attempts at opening the database are stalled until the database can be deleted.

The act of opening a database creates a connection. There MAY be multiple connections to a given database at any given time. Each connection has a closePending flag which initially is set to false.

When a connection is initially created it is in opened state. The connection can be closed through several means. If the connection is GCed or execution context where the connection is created is destroyed (for example due to the user navigating away from that page), the connection is closed. The connection can also be closed explicitly using the steps for closing a database connection. When the connection is closed the closePending flag is always set to true if it hasn't already been.

The IDBDatabase interface represents a connection to a database.

Object Store

An object store is the primary storage mechanism for storing data in a database.

Each database has a set of object stores. The set of object stores can be changed, but can only be changed using a "versionchange" transaction, i.e. in response to a upgradeneeded event. When a new database is created it doesn't contain any object stores.

The object store has a list of records which hold the data stored in the object store. Each record consists of a key and a value. The list is sorted according to key in ascending order. There can never be multiple records in a given object store with the same key.

Every object store has a name. The name is unique within the database to which it belongs. Every object store also optionally has a key generator and an optional key path. If the object store has a key path it is said to use in-line keys. Otherwise it is said to use out-of-line keys.

The object store can derive the key from one of three sources:

  1. A key generator. A key generator generates a monotonically increasing numbers every time a key is needed.
  2. Keys can be derived via a key path.
  3. Keys can also be explicitly specified when a value is stored in the object store.

The IDBObjectStore interface represents an object store. Note however that multiple instances of those interfaces representing the same object store can exist.

Keys

In order to efficiently retrieve records stored in an indexed database, each record is organized according to its key. A value is said to be a valid key if it is one of the following ECMAScript [[!ECMA-262]] types: Number primitive value, String primitive value, Date object, or Array object. An Array is only a valid key if every item in the array is defined and is a valid key (i.e. sparse arrays can not be valid keys) and if the Array doesn't directly or indirectly contain itself. Any non-numeric properties on an Array are ignored, and thus do not affect whether the Array is a valid key. If the value is of type Number, it is only a valid key if it is not NaN. If the value is of type Date it is only a valid key if its [[PrimitiveValue]] internal property, as defined by [[!ECMA-262]], is not NaN. Conforming user agents MUST support all valid keys as keys.

Infinite Number values are valid keys. As are empty Arrays.

Operations that accept keys MUST perform as if each key parameter value, in order, is copied by the structured clone algorithm [[!HTML5]] and the copy is instead used as input to the operation, before proceding with rest of the operation.

This implicit copying step ensures that key values do not change after the operation begins, due to side effects such as ECMAScript [[!ECMA-262]] getters, setters and type conversion functions including toString() and valueOf().

For purposes of comparison, all Arrays are greater than all String, Date and Number values; all String values are greater than all Date and Number values; and all Date values are greater than all Number values. Values of type Number are compared to other Number values numerically. Values of type Date are compared to other Date values chronologically. Values of type String are compared to other values of type String by using the algorithm defined by step 4 of section 11.8.5, The Abstract Relational Comparison Algorithm, of the ECMAScript Language Specification [[!ECMA-262]]. Values of type Array are compared to other values of type Array as follows:

  1. Let A be the first Array value and B be the second Array value.
  2. Let length be the lesser of A's length and B's length.
  3. Let i be 0.
  4. If the ith value of A is less than the ith value of B, then A is less than B. Skip the remaining steps.
  5. If the ith value of A is greater than the ith value of B, then A is greater than B. Skip the remaining steps.
  6. Increase i by 1.
  7. If i is not equal to length, go back to step 4. Otherwise continue to next step.
  8. If A's length is less than B's length, then A is less than B. If A's length is greater than B's length, then A is greater than B. Otherwise A and B are equal.

Note that Arrays that contain other Arrays are allowed as valid keys. In this case the algorithm above runs recursively when comparing the individual values in the arrays.

As a result of the above rules, negative infinity is the lowest possible value for a key. There is no highest possible key value. This is because an Array of any candidate highest key followed by another valid key is even higher.

The terms greater than, less than and equal to are defined in the terms of the above comparisons.

The following examples illustrate the different behaviors when trying to use in-line keys and key generators to save an object to an object store.

Values

Each record is associated with a value. Conforming user agents MUST support any ECMAScript [[!ECMA-262]] value supported by the structured clone algorithm [[!HTML5]]. This includes simple types such as String primitive values and Date objects as well as Object and Array instances, File objects, Blob objects, ImageData objects, and so on. Record values are stored and retrieved by value rather than by reference; later changes to a value have no effect on the record stored in the database.

Key Path

A key path is a DOMString or sequence<DOMString> that defines how to extract a key from a value. A valid key path is one of:

  • An empty DOMString.
  • An identifier, which is a DOMString matching the IdentifierName production from the ECMAScript Language Specification [[!ECMA-262]].
  • A DOMString consisting of two or more identifiers separated by periods (ASCII character code 46).
  • A non-empty sequence<DOMString> containing only DOMStrings conforming to the above requirements.

Spaces are not allowed within a key path.

To evaluate a key path, run the steps for extracting a key from a value using a key path.

Key path values can only be accessed from properties explicitly copied by the structured clone algorithm, as well as the following properties:

  • Blob.size
  • Blob.type
  • File.name
  • File.lastModifiedDate
  • Array.length
  • String.length

Index

It is sometimes useful to retrieve records in an object store through other means than their key. An index allows looking up records in an object store using properties of the values in the object stores records.

An index is a specialized persistent key-value storage and has a referenced object store. The index has a list of records which hold the data stored in the index. The records in an index are automatically populated whenever records in the referenced object store are inserted, updated or deleted. There can be several indexes referencing the same object store, in which changes to the object store cause all such indexes to get updated.

The values in the index's records are always values of keys in the index's referenced object store. The keys are derived from the referenced object store's values using a key path. If a given record with key X in the object store referenced by the index has the value A, and evaluating the index's key path on A yields the result Y, then the index will contain a record with key Y and value X.

Records in an index are said to have a referenced value. This is the value of the record in the index's referenced object store which has a key equal to the index's record's value. So in the example above, the record in the index whose key is Y and value is X has a referenced value of A.

Each record in an index references one and only one record in the index's referenced object store. However there can be multiple records in an index which reference the same record in the object store. And there can also be no records in an index which reference a given record in an object store.

The records in an index are always sorted according to the record's key. However unlike object stores, a given index can contain multiple records with the same key. Such records are additionally sorted according to the index's record's value (meaning the key of the record in the referenced object store).

Every index has a name. The name is unique within index's referenced object store.

Each index also has a unique flag. When this flag is set to true, the index enforces that no two records in the index has the same key. If a record in the index's referenced object store is attempted to be inserted or modified such that evaluating the index's key path on the records new value yields a result which already exists in the index, then the attempted modification to the object store fails.

Each index also has a multiEntry flag. This flag affects how the index behaves when the result of evaluating the index's key path yields an Array. If the multiEntry flag is false, then a single record whose key is an Array is added to the index. If the multiEntry flag is true, then the one record is added to the index for each item in the Array. The key for each record is the value of respective item in the Array.

The IDBIndex interface provides access to the metadata of an index. Note however that multiple instances of those interfaces representing the same index can exist.

Transaction

A transaction is used to interact with the data in a database. Whenever data is read or written to the database it is done by using a transaction.

All transactions are created through a connection, which is the transaction's connection. The transaction has a mode that determines which types of interactions can be performed upon that transaction. The mode is set when the transaction is created and remains fixed for the life of the transaction. The transaction also has a scope that determines the object stores with which the transaction may interact. Transactions have an active flag, which determines if new requests can be made against the transaction. Finally, transactions also contain a request list of requests which have been made against the transaction.

Each transaction has a fixed scope, determined when the transaction is created. A transaction's scope remains fixed for the lifetime of that transaction.

Transactions offer some protection from application and system failures. A transaction may be used to store multiple data records or to conditionally modify certain data records. A transaction represents an atomic and durable set of data access and data mutation operations.

Transactions are expected to be short lived. This is encouraged by the automatic committing functionality described below. Authors can still cause transactions to run for a long time; however, this usage pattern is not generally recommended as it can lead to a bad user experience.

The lifetime of a transaction is as follows:

  1. A transaction is created using IDBDatabase.transaction. The arguments passed determine the scope of the transaction and whether the transaction is read-only. When a transaction is created its active flag is initially set to true.
  2. The implementation MUST allow requests to be placed against the transaction whenever the active flag is true. This is the case even if the transaction has not yet been started. Until the transaction is started the implementation MUST NOT execute these requests; however, the implementation MUST keep track of the requests and their order. Requests may be placed against a transaction only while that transaction is active. If an attempt is made to place a request against a transaction when that transaction is not active, the implementation MUST reject the attempt by throwing a DOMException of type TransactionInactiveError.
  3. Once an implementation is able to enforce the constraints defined for the transaction mode, defined below, the implementation MUST queue up an operation to start the transaction asynchronously. The timing for when this happens is affected by:
  4. Once the transaction has been started the implementation can start executing the requests placed against the transaction. Unless otherwise defined, requests MUST be executed in the order in which they were made against the transaction. Likewise, their results MUST be returned in the order the requests were placed against a specific transaction. There is no guarantee about the order that results from requests in different transactions are returned. Similarly, the transaction modes ensure that two requests placed against different transactions can execute in any order without affecting what resulting data is stored in the database.
  5. A transaction can be aborted at any time before it is finished, even if the transaction isn't currently active or hasn't yet started. When a transaction is aborted the implementation MUST undo (roll back) any changes that were made to the database during that transaction. This includes both changes to the contents of object stores as well as additions and removals of object stores and indexes.
  6. A transaction can fail for reasons not tied to a particular IDBRequest. For example due to IO errors when committing the transaction, or due to running into a quota limit where the implementation can't tie exceeding the quota to a partcular request. In this case the implementation MUST run the steps for aborting a transaction using the transaction as transaction and the appropriate error type as error. For example if quota was exceeded then QuotaExceededError should be used as error, and if an IO error happened, UnknownError should be used as error.
  7. When a transaction can no longer become active, the implementation MUST attempt to commit it, as long as the transaction has not been aborted. This usually happens after all requests placed against the transaction have been executed and their returned results handled, and no new requests have been placed against the transaction. When a transaction is committed, the implementation MUST atomically write any changes to the database made by requests placed against the transaction. That is, either all of the changes MUST be written, or if an error occurs, such as a disk write error, the implementation MUST NOT write any of the changes to the database. If such an error occurs, the implementation MUST abort the transaction by following the steps for aborting a transaction, otherwise it MUST commit the transaction by following the steps for committing a transaction.
  8. When a transaction is committed or aborted, it is said to be finished. If a transaction can't be finished, for example due to the implementation crashing or the user taking some explicit action to cancel it, the implementation MUST abort the transaction.

Transactions are opened in one of three modes. The mode determines how concurrent access to object stores in the transaction are isolated.

readonly
A "readonly" transaction is only allowed to read data. No modifications can be done by this type of transaction. This has the advantage that several "readonly" transactions can run at the same time even if their scopes are overlapping, i.e. if they are using the same object stores. This type of transaction can be created any time once a database has been opened using the IDBDatabase.transaction method.
readwrite
A "readwrite" transaction is allowed to read, modify and delete data from existing object stores. However object stores and indexes can't be added or removed. Multiple "readwrite" transactions can't run at the same time if their scopes are overlapping since that would mean that they can modify each other's data in the middle of the transaction. This type of transaction can be created any time once a database has been opened using the IDBDatabase.transaction method.
versionchange
A "versionchange" transaction is similar to a "readwrite" transaction, however it can additionally create and remove object stores and indexes. It is the only type of transaction that can do so. This type of transaction can't be manually created, but instead is created automatically when a upgradeneeded event is fired.

Any number of transactions opened in "readonly" mode are allowed to r