Namespace localStorage

Namespace localStorage

How to namespace your localStorage on the same domain…

TL;DR

  • Local Storage is prone to name clashes and accidental data overrides
  • Total amount of data that can be stored is limited per domain

persistme lets you namespace your local storage usage across multiple applications and defy the size limit by applying compression to your data.


localStorage was a great addition to the Web Storage APIs letting developers persist some data for an enhanced user experience. It has existed for long and today almost every web application is using this piece of technology to persist some user interactions and more. However due to the fact that the internet has boomed and every traditional desktop application has either retired or is now offering an equally compelling and sophisticated web interface, there has been two pressing limitations grown in local storage that I have come to realise.

  • Inability to separate multi-app usage on same domain
  • Restricted data limit

Let's shed some lights with solutions to these two problem statements.


Multi-app usage on same domain

Since localStorage stores key-value pairs that are domain specific, you’d quickly run into problems if you host multiple apps on the same domain, all of which are making use of local storage to store some of their app specific data. You’d ask why would I host multiple apps on the same domain, right? Well, look at any considerable sized organisation, chances are that they have distributed teams, each of which is taking care of a single aspect from a large user facing application.

For example, take a case of an e-commerce website. There would be teams taking care of their landing pages, one team for product details page, another for product catalog, another for their payment, etc etc, all of which would be hosted on the same myecommerce.com with their own unique sub application paths, /catalog, /product/{id}, /payment etc.

It’s not a surprise that each of these teams may accidentally step on each other’s shoes while storing/retrieving data through localStorage.setItem(key, value) and localStorage.getItem(key) to begin with. Using the same key as the other apps, but having a completely different contextual meaning to it, may mean a broken or even worse, a falsy user interface.

Further more, even if your apps are hosted on different subdomains, chances are that you are setting the domain to root domain of your app in order to overcome same-origin policy restrictions. And thus, your localStorage would behave as if they are on the root domain, taking you back to the base problem.

Solution

A simple solution to this problem is to prefix all your app specific keys with your unique project name and later store/retrieve values using those keys. Every company has internal codenames to their project repos, make use of the same as your localStorage identifier.

// APP 1
const APP_NAME = 'PROJECT_NAME_1';

localStorage.setItem(`${APP_NAME}.userSetting`, 'AWESOME 1');
localStorage.getItem(`${APP_NAME}.userSetting`); // AWESOME 1

// APP 2
const APP_NAME = 'PROJECT_NAME_2';

localStorage.setItem(`${APP_NAME}.userSetting`, 'AWESOME 2');
localStorage.getItem(`${APP_NAME}.userSetting`); // AWESOME 2

Namespace for localStorage

You might think that it’s error prone and problematic to do this each time you want to use localStorage methods. Well, ideally you’d create a wrapper and use the wrapper instead of directly accessing the localStorage object.

// AppStorage.js
const APP_NAME = 'PROJECT_NAME';
const getKey = key => `${APP_NAME}.${key}`;

export const set = (key, value) =>
    localStorage.setItem(getKey(key), value);

export const get = key =>
    localStorage.getItem(getKey(key));

// other wrapper methods
...
// SomeModule.js
import * as AppStorage from './appstorage';

AppStorage.set('userSetting', 'AWESOME');
AppStorage.get('userSetting') // AWESOME

localStorage wrapper with namespace

As you may realise, this simple yet powerful technique can help you avoid accidental production bugs caused due to the name clashes and accidental overrides.


Restricted data limit

I don’t need to explain this in detail. There is a limit to how much data you can squeeze in that local storage for a domain. Every browser has its limit in place and deletes data using some policies. Usually the limit is enough for normal cases. However if your app relies heavily on local storage to cache/persist user and or application data for a seamless user experience, you might run into problems where your data is either cleared or not allowed to be stored further by the browser if you exceed your limit.

Solution

Use a client side compression algorithm like lz-based compression before storing the data in local storage. The performance overheads are negligible, but the data gains are substantial and you’d be able to squeeze in a lot more than you ideally could.

...
// building on to previous example
import LZString from 'lz-string';
const { compress, decompress } = LZString;

export const set = (key, value) =>
    localStorage.setItem(getKey(key), compress(value));

export const get = key =>
    decompress(localStorage.getItem(getKey(key)));
...

Storing compressed values in localStorage


I wrote a small (~2kb) open source JS library, persistme, that includes solution to both these problems including few nice goodies for some common local storage use cases. Do check that out and show some love if you like :)

import { createStorage, createSetting } from 'persistme';

const AppStorage = createStorage('MY_APP1',
    {/*optional defaults*/});

AppStorage.get('theme'); // returns default value if provided
AppStorage.set('theme', 'blue');
AppStorage.get('theme'); // blue

// or even simpler
const UserSetting = createSetting({ storage: AppStorage });

console.log(UserSetting.theme); // blue
UserSetting.theme = 'red';
console.log(UserSetting.theme); // red

Code sample using persistme

mesmerised/persistme
localStorage on steroids :syringe: Namespace your storage even on same domains, defy storage limits with compression and much more :floppy_disk: - mesmerised/persistme

Try it out and bless me later ;)


The article was originally posted on medium.