Custom Data Storage

A review of our storage architecture prior to reading this tutorial will provide helpful background information.

The Storage Client API uses an adapter to save and retrieve data from data store. Because we wanted Finsemble to use local storage, we built the Local Storage Adapter for this purpose. If you want to utilize a data store besides local storage—such as a remote database—you simply need to build an adapter that interfaces the Storage Client API with that data store.

Our adapter is provided as an example: feel free to modify, add to, and customize it for your purposes.

Similar to our example, your adapter must extend our BaseStorage model and override all the required methods in the model (listed below). You can specify different storage adapters by topic at runtime using the setStore method in the Storage Client API. Remember, all storage is partitioned by topic, which is kin to a namespace to prevent key conflicts. All of Finsemble's core storage uses "finsemble" as the topic. You can use any topic you choose, for example your application or a company's name.

If you are using our seed build process, you can add your files to be built in the webpack.files.entries.json file in a similar method to the Local Storage Adapter.

Required methods for your adapter

When building an adapter, remember that Finsemble supports only the key-value store by topic. When you build you adapter, it needs to have the following required methods:

  • Save: Saves a key value pair for a specific topic.
/**
 * Save method.
 * @param {object} params
 * @param {string} params.topic -  A topic under which the data should be stored.
 * @param {string} params.key - The key whose value is being set.
 * @param {any} params.value - The value being saved.
 * @param {function} cb - Callback to be invoked upon save completion.
 */
this.save = function (params, cb) {
    //Your code here
    return cb(null, { status: "success" });
}
  • Get: Gets a key value pair for a specific topic.
/**
 * Get method.
 * @param {object} params
 * @param {string} params.topic - A topic under which the data should be stored.
 * @param {string} params.key - The key whose value is being set.
 * @param {function} cb - Callback to be invoked upon completion.
 */
this.get = function (params, cb) {
    //Your code here
    return cb(null, data);
}
  • Delete: Deletes a key value pair for a specific topic.
/**
 * Delete method.
 * @param {object} params
 * @param {string} params.topic -  A topic under which the data should be stored.
 * @param {string} params.key - The key whose value is being deleted.
 * @param {function} cb - Callback to be invoked upon completion.
 */
this.delete = function (params, cb) {
    //Your code here
    return cb(null, { status: "success" });
};
  • Empty: Deletes everything in the storage.
/**
 * Wipes the storage container.
 * @param {function} cb
 */
this.empty = function (cb) {
    // Your code here
    return cb(null, { status: "success" });
};
  • Keys: Gets a list of all keys for a specific topic.
/**
* Returns all keys stored in localstorage.
* @param {*} params
* @param {string} params.topic - A topic under which the data should be stored.
* @param {*} cb
*/
this.keys = function (params, cb) {
    //Your Code Here
    return cb(null, keys);
};

Configuration

In your Finsemble config, you can specify storage adapters in the storage setting:

"storage": {
    "topicToDataStoreAdapters" : {
    "Finsemble" : "LocalStorageAdapter",
    "Finsemble.workspace" : "LocalStorageAdapter",
    "Finsemble.workspace.cache" : "LocalStorageAdapter"
    },
    "dataStoreAdapters" : {
      "LocalStorageAdapter": "$applicationRoot/adapters/LocalStorageAdapter.js"
    }
  },

You can specify the default adapter using the defaultStorage setting:

"defaultStorage": "LocalStorageAdapter"

Topics

Finsemble partitions storage into three topics to provide a more flexible configuration. The three storage topics are:

  • "Finsemble.workspace": This is the storage for workspace, only accessed when workspaces are initialized or explicitly saved by the user.
  • "Finsemble.workspace.cache": This is the temporary storage for high-frequency updates (e.g., window moves, resizes, opens, closes). Typically this should be assigned to LocalStorageAdapter.
  • “Finsemble”: This is the general topic than containers—anything that is outside of workspace. This topic has a low frequency of usage; very little falls under this topic currently.

Storage config is processed during the initialization of the storage services, so all related config settings will take place before any storage clients are initialized. If a topic doesn't have a storage adapter in the config, it defaults to the "defaultStorage" value. If no "defaultStorage" value exists, it then defaults to localStorage.

You can define your own topics for storage. Newly-defined topics can be mapped to any adapter by inserting a corresponding entry under topicToDataStoreAdapters: the property name (e.g., example-new.workspace) must be the topic and the property value (e.g. LocalStorageAdapter) must be the adapter.

Advanced reading

Reading the documentation of our Storage Client will provide additional context.

Read the Router tutorial to better understand how Finsemble sends and receives messages.