Storage
Several storage APIs are available for Cordova applications. See html5rocks storage overview and tutorial, for a more complete overview and examples.
Each API offers advantages and disadvantages, which are summarized here. You should choose whichever best suits your needs. You can also use several different approaches within a single application for different purposes.
LocalStorage
Local storage provides simple, synchronous key/value pair storage, and is supported by the underlying WebView implementations on all Cordova platforms.
Usage Summary
Local storage can be accessed via window.localStorage
. The following code
snippet shows the most important methods exposed by the returned Storage
object:
var storage = window.localStorage;
var value = storage.getItem(key); // Pass a key name to get its value.
storage.setItem(key, value) // Pass a key name and its value to add or update that key.
storage.removeItem(key) // Pass a key name to remove that key from storage.
For more information, see:
Advantages
- Supported by all Cordova platforms.
- Its simple, synchronous API means it is easy to use.
Disadvantages
- Only stores strings, so complex data structures have to be serialized, and only data that can be serialized can be stored.
- Performs poorly with large amounts of data. In particular:
- The lack of indexing means searches require manually iterating all data.
- Storing large or complex items is slow due to the need to serialize/de-serialize.
- Synchronous API means calls will lock up the user interface.
- Limited total amount of storage (typically around 5MB).
- iOS stores
localStorage
data in a location that may be cleaned out by the OS when space is required.
WebSQL
WebSQL provides an API for storing data in a structured database that can be queried using a standard SQL syntax (specifically, SQLite). As such, it provides all the power (and complexity) of SQL.
It is supported by the underlying WebView on the following Cordova platforms:
- Android
- iOS
Usage Summary
The entry point into creating or opening a database is the window.openDatabase()
method:
var db = window.openDatabase(name, version, displayName, estimatedSize);
- name (string): The unique name of the database, as it will be stored in disk.
- version (string): The version of the database.
- displayName (string): A human friendly name for the database, which the system will use if it needs to describe your database to the user (for example, when requesting permission to increase the size of the database).
- estimatedSize (number): The expected maximum size of the database, in bytes. As the database increases in size, the user may be prompted for permission. If you make a reasonable first guess, the user is likely to be prompted less often.
The returned Database
object provides a transaction()
method (or readTransaction()
to optimize read-only transactions) that let's you create a failure-safe transaction:
var db = window.openDatabase(name, version, displayName, estimatedSize);
db.transaction(function (tx) {
tx.executeSql(sqlStatement, valueArray, function (tx, result) {
console.log(result);
}, function (error) {
console.log(error);
});
});
For more information, see:
For a good introduction to the SQL language, see:
Working with database versions
When opening an existing database, if the specified version does not match
the version of the database, an exception will be thrown and the database
will not open. However, if you specify an empty string for the version, the
database will open regardless of its current version (and you can query the
current version via db.version
). Be wary, however - if the database is
being created, it will be created with its version set to an empty string.
Advantages
- Good performance - data can be indexed to provide fast searches, and asynchronous API means it doesn't lock up the user interface.
- Robustness from using a transactional database model.
- Support for versioning.
Disadvantages
- Not supported by all Cordova platforms.
- More complex to work with than LocalStorage or IndexedDB.
- The API is deprecated. It is unlikely to ever be supported on platforms that don't currently support it, and it may be removed from platforms that do.
- Imposes a rigid structure that must be defined up-front.
- Limited total amount of storage (typically around 5MB).
IndexedDB
The goal of the IndexedDB API is to combine the strengths of the LocalStorage and WebSQL APIs, while avoiding their weaknesses. IndexedDB lets you store arbitrary JavaScript objects (provided they are supported by the structured clone algorithm), indexed with a key. It provides some of the benefits of SQL tables, without constraining the structure or needing to define it up front.
IndexedDB provides a simple and easy to understand data model, much like LocalStorage. But unlike LocalStorage, you can create multiple databases, with multiple stores per database, and its asynchronous API and search indexes provide performance benefits.
IndexedDB is supported by the underlying WebView on the following Cordova platforms:
- Windows (with some limitations)
- Android (4.4 and above)
Windows Limitations
Windows platform support for IndexedDB is incomplete. For example, it lacks the following features:
- Not available in web workers.
- Doesn't support array keyPaths.
- Doesn't support array keys.
- Doesn't support object lookup via compound index.
Usage Summary
- IndexedDB works asynchronously - you request a particular database operation, then get notified of the result via a DOM event.
- When you make a request, you get a request object, which provides
onerror
andonsuccess
events, as well as properties such asresult
,error
andreadyState
.
The following code snippet demonstrates some simple usage of IndexedDB:
var db;
var databaseName = 'myDB';
var databaseVersion = 1;
var openRequest = window.indexedDB.open(databaseName, databaseVersion);
openRequest.onerror = function (event) {
console.log(openRequest.errorCode);
};
openRequest.onsuccess = function (event) {
// Database is open and initialized - we're good to proceed.
db = openRequest.result;
displayData();
};
openRequest.onupgradeneeded = function (event) {
// This is either a newly created database, or a new version number
// has been submitted to the open() call.
var db = event.target.result;
db.onerror = function () {
console.log(db.errorCode);
};
// Create an object store and indexes. A key is a data value used to organize
// and retrieve values in the object store. The keyPath option identifies where
// the key is stored. If a key path is specified, the store can only contain
// JavaScript objects, and each object stored must have a property with the
// same name as the key path (unless the autoIncrement option is true).
var store = db.createObjectStore('customers', { keyPath: 'customerId' });
// Define the indexes we want to use. Objects we add to the store don't need
// to contain these properties, but they will only appear in the specified
// index of they do.
//
// syntax: store.createIndex(indexName, keyPath[, parameters]);
//
// All these values could have duplicates, so set unique to false
store.createIndex('firstName', 'firstName', { unique: false });
store.createIndex('lastName', 'lastName', { unique: false });
store.createIndex('street', 'street', { unique: false });
store.createIndex('city', 'city', { unique: false });
store.createIndex('zipCode', 'zipCode', { unique: false });
store.createIndex('country', 'country', { unique: false });
// Once the store is created, populate it
store.transaction.oncomplete = function (event) {
// The transaction method takes an array of the names of object stores
// and indexes that will be in the scope of the transaction (or a single
// string to access a single object store). The transaction will be
// read-only unless the optional 'readwrite' parameter is specified.
// It returns a transaction object, which provides an objectStore method
// to access one of the object stores that are in the scope of this
//transaction.
var customerStore = db.transaction('customers', 'readwrite').objectStore('customers');
customers.forEach(function (customer) {
customerStore.add(customer);
});
};
};
function displayData() {
}
For more information, see:
Advantages
- Good performance - asynchronous API won't block the UI, and indexing provides good search performance.
- Simple data model easier to learn than SQL.
- More flexible structure than WebSQL.
- Multiple databases and object stores provides more structure than LocalStorage.
- Robustness from using a transactional database model.
- Support for versioning.
Disadvantages
- Not supported on iOS.
- Complex API with nested callbacks.
- Limited total amount of storage (typically around 5MB).
Plugin-Based Options
FileSystem API
The FileSystem API was a W3C spec that was implemented by Chrome, but not other browsers. It provides APIs to store and retrieve data on the local file system, and is described in some detail in an excellent html5rocks article. While the API is not supported natively by any Cordova platform, the File plugin provides an extensive implementation that is available across all Cordova platforms.
SQLite Plugin
The SQLite plugin provides an API virtually identical to WebSQL described above. The main differences are:
- It is available with support for the Windows platform.
- It effectively has no size limitations.
It is available in the following variations:
- cordova-sqlite-storage - core version that includes its own sqlite3 implementation. It supports iOS, Android & Windows platforms.
- cordova-sqlite-ext - extended version with additional features including REGEXP support on Android and iOS.
- cordova-sqlite-evfree - similar to cordova-sqlite-ext but with improved memory handling. Available under GPL v3 or commercial license.
Other Plugins
Search Cordova plugins for other plugins that provide alternative storage options.