There are 3 major types of storage is browsers up to date:
- IndexedDB. The future.
- localStorage. Limited in size and can be observed through the
storage
event. - WebSQL. Dropped by the W3C but it's still very present in the wild, with notable platforms like iOS and PhoneGap.
Green: IndexedDB; blue: WebSQL; yellow: localStorage
In the mobile world local cache and storage is very important for various reasons:
- faster loading times
- unreliable connections
- offline use
My particular use case is an app to be used "in the field" where you probably don't have a connection and you need to do data entry. My solution is to save to a local storage and sync to the server when a connection is available. The 5 MB of localStorage may not be enough in my case, but I won't be able to tell until the app is used by real users.
- Is there enough interest in this considering the availability of
localStorage
? - Is it too early to use these technologies?
The goal is to use conditional loading to load one of 3 or 4 sub-modules: indexeddb, websql, localstorage and a fallback to memory.
- IndexedDB uses a key-based storage organized in "object stores".
- WebSQL uses SQL tables. WebSQL can be used as a fallback for object stores by matching a table to a store and using tables with two columns: a
key
and avalue
column which saves the object serialized as JSON. - IndexedDB uses structured cloning which is similar to
JSON.parse(JSON.stringify(obj))
.
- Is falling back to localStorage a good idea considering the limited size and observability?
- Is the whole concept OK or is it too hackish?
- Should this include over-the-wire databases via some protocol?
The API aims to find the lowest common denominator between all implementations. A database requires:
- A
name
. Required by both IndexedDB and WebSQL - A
version
. Required by both IndexedDB and WebSQL - A structure, which means the name of each object store, tied to the version. Required by both IndexedDB and WebSQL
- A
size
in bytes. Required by WebSQL
A Storage
constructor receives all these parameters and creates an instance with properties with the names of each object store:
var storage = new Y.Storage({
name: 'my database',
version: 1,
size: 5242880,
stores: ['fooStore', 'barStore']
});
Assert.isObject(storage.fooStore); // true
Assert.isObject(storage.barStore); // true
A Storage
instance also has a close
method that closes the database connection/session.
A Store
object is created for each requested store in a property of the same name. A store can perform the following actions:
get
. Retrieve an object by keyput
. Insert or update an object by keyremove
. Remove an object by keyclear
. Remove all objects from the storecount
. Count the objects in the store
All methods return a promise.
Example:
storage.fooStore.put('some key', { foo: 'bar' }).then(...);
- Should
Storage
inherit fromBase
in order to use attributes for configuration and maybe fireclose
orerror
events? Aversionchange
event is very important.
Thank you very much for the feedback guys!
Some answers:
@ItsAsbreuk
Actually you can't do that. In order to change the size and the structure, you need close the database and open a new one with a different version number. Abstracting over that seems hard and impractical.
Indeed, IndexedDB lets you do that and Storage uses JSON to store objects and arrays in WebSQL.
Absolutely! WebSQL should already do it in my implementation since it passed the object through
JSON.stringify
which looks fortoJSON
. The IndexedDB side should look fortoJSON
.@caridy
I'd like to include anything local, including web app wrappers like PhoneGap or any other that I find. The reason why I didn't include over-the-wire implementations is that I want to leave the door open for different syncing/conflict resolution algorithms. I want to be able to have Models that sync to Storage and, when available, sync to the server via a REST-like API.
Basically my idea so far has been to handle this at the Model level.