The Data Store API provides a powerful, flexible storage mechanism for KaiOS applications to use to store and share data. It is basically an intermediary store to allow multiple applications to share data between one another quickly, efficiently, and securely, in spite of differences between API data structures, formats, etc. Data Store API concepts and usage
The Data Store API was created to allow multiple KaiOS apps, with potentially different data structures and storage mechanisms, to create, maintain and share the same data objects efficiently between one another. Each app can then import the data into its own local IndexedDB to index according to their specific query needs. This is not necessary however, and you can just write directly to the Data Store API data store.
Note: The Data Store API is available in Web Workers
There are several possible reasons to use this API, including:
- It lets an application create data that it can then share with other applications.
- It lets multiple applications supply data to the same data store.
- It supports read-only data stores such as Facebook contacts (that is, it lets one app create and maintain the data while other apps can read the data from the store).
- It supports read/write data stores, so that multiple applications can contribute data to the same data store. An example would be the device's standard contacts, which can be updated by both the built-in Contacts app and third-party apps.
- It supports keeping an application-locale cache of a data store, with notifications to let the app know when there have been changes to the master data store so that the cache can be updated.
This Data Store API datastore is owned by a specific app (as specified by the datastores-owned field in the app's manifest — see Manifest_fields, below), and this ownership gives the app the right to populate the data store and declare whether the data store is
readonly (meaning other apps can only read the data) or
readwrite (meaning other apps can modify the data as well as read it.) Other apps can gain access to a datastore by naming it in the
datastores-access field of their app manifest.
When an app wants to access a datastore, it has to call
Navigator.getDataStores(); The result value of this method is a
Promise object that will resolve with an array of
DataStore objects. From these
DataStore objects, the app can read and modify values using various methods of the
DataStore such as
Note: The Data Store API currently has no imposed limitations on storage space. This is likely to be updated in the near future.
When multiple apps make changes to a Data Store, it could create conflicts. However, any change you make (using
DataStore.add(), etc.) is given a
revisionId, a UUID received in the change event fired whenever a operation is performed on the Data Store by any app with access to it. This can be read from the
revisionId property can be included as an optional parameter in the
DataStore.sync() methods. Doing so basically uses that
revisionId as a conflict avoider — the operation is aborted if that revisionId is not the last one known by the Data Store (i.e. if another app has made a more recent change).
When the change event is fired, it receives a
DataStoreChangeEvent object, giving the app access to:
- DataStoreChangeEvent.revisionId: The last known revisionId.
- DataStoreChangeEvent.id: The key of the changed object, which can be null if the operation was a clear().
- DataStoreChangeEvent.operation: The operation that was performed — add(), remove(), etc.
- DataStoreChangeEvent.owner: The manifest URL of the app that performed this operation.
When an app wants to see what has changed, it can do it by asking for the ‘delta’ between the last known revisionId and the current one. This is done using the DataStore.sync() method. You can allow your apps to deal with data changes by running a sync() on application startup and onchange, passing it the current revisionId to check against.
As hinted at above, the Data Store API is not responsible for filtering data or building indexes; instead it leaves this up to the the app's local storage mechanism (usually an IndexedDB); The Data Store API simply allows the local indexes to be kept up to date, via the DataStoreCursor object, created when sync() is invoked.
The data store owner's manifest MUST include the datastores-owned field to claim ownership, for example:
You can include multiple properties to represent different data stores, and each one can use readonly/readwrite to specify whether the data store can be read/modified by other applications. A description is also included to describe the purpose of the data store.
Other applications that want to access (not own) the data store must include the datastores-access field, for example:
Without this field being specified, the default behavior is "no access". Again, multiple properties can be included if you want to access multiple data stores, and an access of readonly or readwrite can be set to declare what access type is needed by the app.
In terms of permissions, the Data Store owner always wins against the 3rd party apps. if the owner declares "readonly": true in its manifest, any 3rd party app will be in readonly mode also, even if they declare "access": "readwrite" in their manifests. Of course, this isn’t much use if the owner allows the Data Store to be modified by third party apps, so for the moment, Data Store is a certified API. It is likely to come down to privileged when the security model is finalized.
Note: Remember also that in such cases you need to use the type field in your manifest to explicitly declare that your app is an internal/certified app: "type": "certified".
The Data Store API contains the following interfaces:
The DataStore interface represents a retrieved set of data, and includes standard properties for accessing the store's name, owner, etc., methods for reading, modifying and syncing the data, and the onchange event handler for reacting to changes to the data.
This interface allows the app to iterate through a list of DataStoreTask objects representing the change history of the data store, for use when synchronising the data.
This interface represents the event related to a record changed in the data store, i.e. this is returned once a change is made and the change event is fired (see DataStore.onchange for the handler), for use when synchronizing individual changes.
This interface represents a record changed in the data store when a DataStoreCursor is used to iterate through the data store's change history.
We have written a couple of examples that go together, to explain how different apps can make different use of the same data store:
- The Data Store Contacts Editor creates a data store called 'contacts' on the device it is installed on, adds some default data to it, and allows the user to add new contacts to the store and delete contacts.
- The Data Store Contacts Viewer has readonly access to the 'contacts' data store; it grabs data from it and displays it in a user interface, along with a Google map of the selected user's location, displayed in an
<iframe>to get around certified app CSP restrictions.
Keep referring to these examples and read Using the Data Store API for explanations and code samples.
Note: Be aware that to test data store examples you need to make sure your apps are internal/certified (see above for manifest fields needed), and use App Manager or WebIDE to simulate an environment where a internal/certified app can be run. Follow these links to find out how to do this using each tool: App Manager: Debugging Certified Apps and WebIDE: Debugging Certified Apps.