Skip to content

@spfxappdev/storage


npm npm bundle size npm

@spfxappdev/storage is a library to handle the javascript localStorage and sessionStorage in a simple way. It comes with a built-in expiration logic (per default 60 minutes). It is possible to delete the entire storage or only a specific one via URL parameters. The included decorators simplify the application and keep the code short and simple, because you only have to implement the logic (of your method) itself and not the caching (see decorators section).

Installation


npm i @spfxappdev/storage

Usage


Depending on which type of caching you want to use (localStorage or sessionStorage) you must first import the class(es). But regardless of the type, the logic and handling is the same for both classes.

1. Import the storage class(es) into your project


import { LocalStorage, SessionStorage } from '@spfxappdev/storage';

2. You can create an instance now.


const sessionCache = new SessionStorage();
const localCache = new LocalStorage();

3. Set/Get Data


Set/Get Data Example
sessionCache.set("mySessionStorageKey", "mySessionStorageValue");
let dataFromCache = sessionCache.get("mySessionStorageKey");
console.log(dataFromCache); //RESULT: mySessionStorageValue

const myObj = {
    name: '@spfxappdev/storage',
    install: 'npm i @spfxappdev/storage',
    author: 'SPFxAppDev'
};

localCache.set("myLocalStorageKey", myObj);
console.log(myObj); //RESULT: (1)
  1. { name: '@spfxappdev/storage', install: 'npm i @spfxappdev/storage', author: 'SPFxAppDev' }

4. Get data or set data with callback function if not available


let dataFromCache = localCache.get("myLocalStorageKey", () => {
    //Note: (1) 
    return {
        name: '@spfxappdev/storage',
        install: 'npm i @spfxappdev/storage',
        author: 'SPFxAppDev'
    }; 
});
  1. If the storage key does not exist or the cache has expired, the callback function is executed. The return value of this callback function is used to store the value in the cache with the specified key.

5. Custom settings


The constructor of both classes LocalStorage and SessionStorage can be given additional configuration options.

Options (IStorageSettings)

Name Type Required Description Default Value
UrlParameter IStorageUrlParameters Yes Settings for clearing cache via URL parameters. See the section Clear storage via URL parameters { RefreshAll: 'ResetCache', RefreshOnly: 'ResetOnly' }
DefaultTimeToLife number Yes The timeToLife option determines when the cache should expire (in minutes). 60
KeyPrefix string Yes The prefix that is inserted before the cacheKey SPFxAppDev_

Options (IStorageUrlParameters)

Name Type Required Description
RefreshAll string Yes The name of the URL parameter that should clear all stored values from the cache
RefreshOnly string Yes The name of the URL parameter that should clear one or more specified keys from the cache

Override default settings per instance


If you want to override the default settings per instance, you can do it like this:

Override default settings per instance
const sessionCache = new SessionStorage({
    UrlParameter: {
        RefreshAll: 'ResetCacheSession',
        RefreshOnly: 'ResetSessionOnly'
    },
    DefaultTimeToLife: 30,
    KeyPrefix: 'SPFxAppDevSession_'
});

const localCache = new LocalStorage({
    UrlParameter: {
        RefreshAll: 'ResetCacheLocal',
        RefreshOnly: 'ResetLocalOnly'
    },
    DefaultTimeToLife: 120,
    KeyPrefix: LocalStorage.DefaultSettings.KeyPrefix // (1)
});
  1. Use the default value of LocalStorage if you do not want to overwrite it

Override default settings globally


If you want to override the default settings, you can do it like this:

Override default settings globally
const defaultLocalStorageSettingsOverride: IStorageSettings  = {
    UrlParameter: {
        RefreshAll: 'ResetCacheLocal',
        RefreshOnly: 'ResetLocalOnly'
    },
    DefaultTimeToLife: 120,
    KeyPrefix: 'SPFxAppDevLocal_'
};

LocalStorage.DefaultSettings = defaultLocalStorageSettingsOverride;

// (1)
const defaultSessionStorageSettingsOverride: IStorageSettings = {...SessionStorage.DefaultSettings, ...{ 
    KeyPrefix: "SPFxAppDevSession_", 
    DefaultTimeToLife: 5,
}};

SessionStorage.DefaultSettings = defaultSessionStorageSettingsOverride;
  1. Use the spread operator to override only some options

Info

This only works if the settings were overwritten first and then the instances were created. Also, you then have to make sure that the overwritten code is imported everywhere (e.g. in the boot file).

API


The methods are the same regardless of the type of caching (local or session) you use.

constructor


constructor(customSettings?: IStorageSettings)

When you create a new object instance, you can pass the optional parameter customSettings. See the usage section on how to create a new instance or the custom settings section to pass your custom settings options.

set


set(cacheKey: string, cacheValue: any, timeToLife: number = 60)

This method stores the passed cacheValue with the given cacheKey in the localStorage or sessionStorage container. The timeToLife argument is optional (default = 60 minutes) and determines when the cache should expire (in minutes). If a value with the cacheKey already exists, it will be overwritten with the new cacheValue.

After the value has been set, two keys exist in the respective storage. The second one has the suffix _expire which contains the timestamp as a value when the cache expires.

For example, if you store a value in sessionStorage in this way:

sessionCache.set("mySessionStorageKey", "mySessionStorageValue");

The storage will then looks like this:

Key Value
SPFxAppDev_mySessionStorageKey mySessionStorageValue
SPFxAppDev_mySessionStorageKey_expire 1674594394115

Info

You can change the prefix as described in the custom settings section

Warning

When objects are stored in the storage, they are serialized via JSON.stringify(). This means that all methods and properties that only have a getter will not be stored. In reverse it means that in storage.get() these methods and getter properties are also not present.

get


get(cacheKey: string, delegateFunction: Function|null = null, timeToLife: number = 60)

This method returns the stored value of the specified cacheKey. The cacheKey corresponds to the same value as specified in the set method. If the cacheKey does not exist, or the key with the suffix _expired does not exist, or the cache has expired AND the delegateFunction is passed, then this function is executed and the return value of this function is used to store the value in the specified cacheKey. If the delegateFunction is not passed and the cacheKey does not exist, or the key with the suffix _expired does not exist, or the cache has expired, null is returned. The timeToLife (in minutes) argument is also optional and is used if delegateFunction is passed AND the cache value is (re)set.

Info

Each cacheKey is stored with a KeyPrefix. If different instances exist that have different KeyPrefix values, only the value that has exactly this KeyPrefix is returned. That means if there is an instance that uses one as KeyPrefix and another that uses two as KeyPrefix, when the set method is executed with the same cacheKey (e.g. test), two different "storage" keys will be created. So the keys one_test and two_test exist. The get("test") method then returns only the value corresponding to the {KeyPrefixOfCurrentInstance}_{cacheKey}. => instanceWithPrefixOne.get('test') //RETURNS 'test with keyPrefix "one" and instanceWithPrefixTwo.get('test') //RETURNS 'test with keyPrefix "two"

Warning

When objects are stored in the storage, they are serialized via JSON.stringify(). This means that all methods and properties that only have a getter will not be stored. In reverse it means that in storage.get() these methods and getter properties are also not present.

remove


remove(cacheKey: string)

Removes the cached value from storage with the specified cacheKey. The cacheKey corresponds to the same value as specified in the set method.

Info

Each cacheKey is stored with a KeyPrefix. If different instances exist that have different KeyPrefix values, only the value that has exactly this KeyPrefix is removed.

clear


clear()

Removes any cached value from storage. But only the values that start with the KeyPrefix (=were set with this instance).

exists


exists(cacheKey: string)

Checks if an item exist in storage cache with specified cacheKey.

Warning

this is the generell getItem-Methode of the cache store. You have to pass the KeyPrefix for the cacheKey to return the values that you stored via the set method. Or use the get method instead.

getStorageKeys


getStorageKeys()

Returns all cacheKeys as string array that start with the KeyPrefix but without the _expired keys.

Decorators


The caching decorators are helpful if you want to achieve a lot with less code and also fast. They help you to program your methods with logic only, without having to take caching into care. The decorators automatically return the cached value if it exists and has not expired (without calling the logic of the method). If there is nothing in the cache or the cache has expired, the logic of the method is called and the return value is automatically written to the cache.

In order to better understand how decorators work, I recommend reading this article.

Note

Simple definition: An ES2016 decorator is an expression which returns a function and can take a target, name and property descriptor as arguments. You apply it by prefixing the decorator with an @ character and placing this at the very top of what you are trying to decorate. Decorators can be defined for either a class, a method or a property.

Let's compare the same code without decorators and with decorators. The logic is not changed, but the result is the same:

Simple class WITHOUT decorators
class MyExampleClass {

    private sesssionStorage: SessionStorage

    constructor() {
        this.sesssionStorage = new SessionStorage();
    }

    public getDummyDataPromise(): Promise<string> {

        this.sesssionStorage.logger.log("getDummyDataPromise START");
        const cacheKey: string = "getDummyDataPromise";
        const cacheData = this.sesssionStorage.get(cacheKey);

        if(cacheData != null) {
            this.sesssionStorage.logger.log("getDummyDataPromise get from cache");
            return Promise.resolve(cacheData);
        }

        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = "This data is from 5s Promise";
                this.sesssionStorage.logger.log("getDummyDataPromise get from Promise");
                this.sesssionStorage.set(cacheKey, data);
                resolve(data);
            }, 5000);
        });
    }
}
(Same) Simple class WITH decorators
class MyExampleClass {
    @sessionCache({
        key: "getDummyDataPromise"
    })
    public getDummyDataPromise(): Promise<string> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = "This data is from 5s Promise";
                resolve(data);
            }, 5000);
        });

    }
}

Needless to say, decorators save a lot of time, reduce the number of lines (29 lines vs. 14 lines (~50% less)) and improve readability.

How to use decorators


Info

To use the method decorators, you must set the experimentalDecorators property in your tsconfig.json to true.

Here is a list of all available method decorators:

Decorator name Description
@sessionCache Return the cached value from sessionStorage (by the specified cacheKey) or store the return value of the method if the cache has expired or is not exists
@localCache Return the cached value from localStorage (by the specified cacheKey) or store the return value of the method if the cache has expired or is not exists
@clearSessionCache Removes the cached value from the sessionStorage (by the given cacheKey)
@clearLocalCache Removes the cached value from the localStorage (by the given cacheKey)

In order to use the decorators, they must be imported

import decorators
export { sessionCache, localCache, clearSessionCache, clearLocalCache  } from '@spfxappdev/storage';

@sessionCache


The storageCache decorator stores the returned value of the method in the sessionStorage with the specified key. The code of the method is executed only if the cache has expired or does not exist. Additional options can be passed to the decorator.

Options (ISessionStorageDecoratorOptions<T>)

Name Type Required Description Default Value
key string | KeyFunction Yes A cacheKey as a string to be used for storage. Or KeyFunction, a function that returns a string. Here you can find more information about this function. -
timeToLife number No The timeToLife option is optional and determines when the cache should expire (in minutes). SessionStorage.DefaultSettings.DefaultTimeToLife see here
keyPrefix string No The prefix that is inserted before the cacheKey SessionStorage.DefaultSettings.KeyPrefix see here
sourceObj<T> T No If convertFromCache option is not set but sourceObj is, a new instance of T is created (=> parameterless public costructor() needed). After that all properties are set via Object.assign. See samples -
convertFromCache(cachedValue: any) function No When objects are stored in the storage, they are serialized via JSON.stringify(). This means that all methods and properties that only have a getter will not be stored. In reverse it means that in storage.get() these methods and getter properties are also not present. If these methods and properties are to be returned, you can implement your own logic through this function that converts the cached value to the required object. See samples -

@localCache


The localCache decorator stores the returned value of the method in the localStorage with the specified key. The code of the method is executed only if the cache has expired or does not exist. Additional options can be passed to the decorator.

Options (ILocalStorageDecoratorOptions<T>)

Name Type Required Description Default Value
key string | KeyFunction Yes A cacheKey as a string to be used for storage. Or KeyFunction, a function that returns a string. Here you can find more information about this function. -
timeToLife number No The timeToLife option is optional and determines when the cache should expire (in minutes). LocalStorage.DefaultSettings.DefaultTimeToLife see here
keyPrefix string No The prefix that is inserted before the cacheKey LocalStorage.DefaultSettings.KeyPrefix see here
sourceObj<T> T No If convertFromCache option is not set but sourceObj is, a new instance of T is created (=> parameterless public costructor() needed). After that all properties are set via Object.assign. See samples -
convertFromCache(cachedValue: any) function No When objects are stored in the storage, they are serialized via JSON.stringify(). This means that all methods and properties that only have a getter will not be stored. In reverse it means that in storage.get() these methods and getter properties are also not present. If these methods and properties are to be returned, you can implement your own logic through this function that converts the cached value to the required object. See samples -

@clearSessionCache


With the clearSessionCache decorator a value with the given key can be removed from the sessionStorage. Additional options can be passed to the decorator.

Options (IClearSessionStorageDecoratorOptions)

Name Type Required Description Default Value
key string | KeyFunction Yes The cacheKey (without keyPrefix) that should be removed from the storage. The key can be a string or KeyFunction (a function that returns a string) Here you can find more information about this function. -
keyPrefix string No The prefix that is inserted before the cacheKey SessionStorage.DefaultSettings.KeyPrefix see here
when function No when is an optional function which expects a boolean as return value. If true is returned, the cacheKey will be removed from the storage, otherwise not. In the function the this.-pointer can be used to access the class instance. In addition, all other passing parameters of the method to which the decorator is applied are also present (Exactly the same as with the KeyFunction or see samples). -

@clearLocalCache


With the clearLocalCache decorator a value with the given key can be removed from the localStorage. Additional options can be passed to the decorator.

Options (IClearLocalStorageDecoratorOptions)

Name Type Required Description Default Value
key string | KeyFunction Yes The cacheKey (without keyPrefix) that should be removed from the storage. The key can be a string or KeyFunction (a function that returns a string) Here you can find more information about this function. -
keyPrefix string No The prefix that is inserted before the cacheKey LocalStorage.DefaultSettings.KeyPrefix see here
when function No when is an optional function which expects a boolean as return value. If true is returned, the cacheKey will be removed from the storage, otherwise not. In the function the this.-pointer can be used to access the class instance. In addition, all other passing parameters of the method to which the decorator is applied are also present (Exactly the same as with the KeyFunction or see samples). -

KeyFunction


This type is a function which expects a string as return value. In the function the this.-pointer can be used to access the class instance. In addition, all other passing parameters of the method to which the decorator is applied are also present

Example with key as function
class MyExampleClass {

    private cacheKey: string = 'getDummyDataPromise';

    @sessionCache({
        key: (param1: string, param2: number, paramN: any): string => {
            return (this as MyExampleClass).cacheKey;
        }
    })
    public getDummyDataPromise(param1: string, param2: number, paramN: any): Promise<string> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = "This data is from 5s Promise";
                resolve(data);
            }, 5000);
        });

    }
}
As you can see in line 6, the parameters are the same as in the method (line 10). In line 7 you can see how to access the current class instance.

Decorator examples


Simple local cache decorator example


Simple local cache decorator example
class MyExampleClass {

    @localCache({
        key: "getDummyDataPromise"
    })
    public getDummyDataPromise(param1: string, param2: number, paramN: any): Promise<string> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = "This data is from 5s Promise";
                resolve(data);
            }, 5000);
        });

    }
}

Local cache decorator with key prefix


Local cache decorator with key prefix
class MyExampleClass {

    @localCache({
        key: "getDummyDataPromise",
        keyPrefix: 'HelloSPFx_' // (1)
    })
    public async getDummyDataPromise(param1: string, param2: number, paramN: any): Promise<string> {
        return await anyPromiseFunc(param1, param2, paramN);
    }
}
  1. The generated storage keys are HelloSPFx_getDummyDataPromise and HelloSPFx_getDummyDataPromise_expire

Assign properties to class via sourceObj


Assign properties to class via sourceObj
class SimpleClassToConvert {
    public prop1: string;
    public prop2: string;
    public prop3: number;

    public setDefaultProps(): void {
        this.prop1 = "Hello";
        this.prop2 = "SPFxAppDev";
        this.prop3 = 42;
    }

    public anotherFunc() {

    }
}

class MyConvertFromSourceSampleClass {

    @localCache<SimpleClassToConvert>({
        key: "MyConvertFromSourceSampleClass_getDummyDataPromise",
        sourceObj: SimpleClassToConvert
    })
    public getDummyDataPromise(): Promise<SimpleClassToConvert> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = new SimpleClassToConvert();
                data.setDefaultProps();
                resolve(data);
            }, 5000);
        });

    }
}

const convertFromSource = new MyConvertFromSourceSampleClass();
(async function() {

    const simpleClassObj = await convertFromSource.getDummyDataPromise();
    //Working with the simpleClassObj... (1)
})();
  1. No matter if it comes from the cache or from the method, in both cases the object is returned with all methods and properties.

Assign properties to class via convertFromCache-method


Assign properties to class via convertFromCache-method
class DataClassToConvert {
    public readonly id: string;

    public get isPublished(): boolean {
        return this.version > 0;
    }

    public version: number;

    private constructor(id: string) {
        this.id = id;
        this.version = 0;
    }

    public static ConvertFromCacheCollection(cachedValue: any): DataClassToConvert[] {

        const resultValue: DataClassToConvert[] = [];
        (cachedValue as Array<any>).forEach((val: any) => {

            const instance = new DataClassToConvert(val.id);
            (Object as any).assign(instance, val);
            resultValue.push(instance);
        });

        return resultValue;
    }

    public static CreateDummyData(): DataClassToConvert[] {
        const resultValue: DataClassToConvert[] = [];

        const c1 = new DataClassToConvert("abc");
        resultValue.push(c1);

        const c2 = new DataClassToConvert("def");
        c2.version = 1;
        resultValue.push(c2);       

        return resultValue;
    }

}

class MyConvertFromCacheSampleClass {

    @localCache({
        key: "MyConvertFromCacheSampleClass_getDummyDataPromise",
        convertFromCache: (cachedValue) => {
            return DataClassToConvert.ConvertFromCacheCollection(cachedValue);
        }
    })
    public getDummyDataPromise(): Promise<DataClassToConvert[]> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const data = DataClassToConvert.CreateDummyData();
                resolve(data);
            }, 5000);
        });

    }
}

const convertFromCache = new MyConvertFromCacheSampleClass();
(async function() {

    const dataClassToConvertObj = await convertFromCache.getDummyDataPromise();
    //Working with the dataClassToConvertObj... (1)
})();
  1. No matter if it comes from the cache or from the method, in both cases the object of type DataClassToConvert is returned with all methods and properties.

Clear session cache


Clear session cache
class MyExampleClass {

    @clearSessionCache({
        key: "aKeyWithoutPrefix",
        keyPrefix: '' // (1)
    })
    public getDummyDataPromise(param1: string, param2: number, paramN: any): Promise<string> {
        return await anyPromiseFunc(param1, param2, paramN);
    }
}
  1. The "key prefix" is empty. This is useful if you want to remove a value from memory that has no prefix (for example from another application)

@clearLocalCache combined with @localCache


@clearLocalCache combined with @localCache
class MyExampleClass {

    @clearLocalCache({
        key: "myKey",
        when: (param1: string, refreshCache: boolean) => {
            return refreshCache; // (1)
        }
    })
    @localCache({ // (2)
        key: "myKey",
    })
    public getDummyDataPromise(param1: string, refreshCache: boolean): Promise<string> {
        return await anyPromiseFunc(param1, param2, paramN);
    }
}
  1. The parameters are the same as in the method (line 12). In the function the this.-pointer can be used to access the class instance
  2. The order is important. First you have to check if the cache should be cleared. Otherwise the clearLocalCache decorator will not be executed because the localCache method will not execute a method when returned value from the cache.

Clear storage via URL parameters


Via the URL parameters the stored data can be deleted either completely or only certain ones. The names of the parameters are defined globally or per instance (see custom settings).

Info

Deleting via URL parameters only works if the cache has been called at least once on the page via the get method. For example, if only the SessionStorage.get() is used, then only sessionStorage will be cleared, but not localStorage. If nothing is used, then nothing is cleared. It is also applied only to the instances that use the same URL parameter configurations (IStorageSettings.UrlParameter) AND keyPrefix.

Delete all via URL


By default, the URL parameter ResetCache is used for both LocalStorage and SessionStorage. If you want to set another one, you have to override it (see custom settings).

If you want to clear the complete cache, you have to specify the corresponding URL parameter. The following example refers to the default URL parameter ResetCache. The value of the parameter does not matter, it just has to be present in the URL

http://localhost:1234?ResetCache=1

Remove only specific values via URL


By default, the URL parameter ResetOnly is used for both LocalStorage and SessionStorage. If you want to set another one, you have to override it (see custom settings).

If you want to clear a specific value, you have to specify the corresponding URL parameter. The following example refers to the default URL parameter ResetOnly.

http://localhost:1234?ResetOnly=mySessionStorageKey

You can delete multiple cached values by separating the keys with a comma

http://localhost:1234?ResetOnly=mySessionStorageKey,myLocalStorageKey