Skip to Content
FrontendStructureKey value store (KVS)

Key value store (KVS)

Introduction

KVS stands for Key Value Store and it’s a way to store data following a key-value structure


Structure of a KVS

For the different projects, to standardize the KVS, the IDs of fields (setted with pfqcn) will be used as keys of KVS to become more precise, facilitate and standardize the KVS structures.

  • key : id of the field + . + action
    • Example: pfqcn_phpros_posBundle_informationForm_client_input.show
{ "id": 3, "key": "pfqcn_phprpos_posBundle_demo_informationForm_deliveryDate_field.show", "value": "1", "description": "Hide/Show the delivery date input (0) to hide (1) to show" "shortcode": "KVS", "user": { "id": 1, "shortcode": "UU", "toString": "admin UU1", "uniqueId": "UU1", }, "corporation": null, "clearanceLevel": 7, "slug": "38401502-a075-11ee-b979-067e394b3036", "fieldFromKey": "show", "uniqueId": "KVS3", "enabled": true, "disabled": false }

How to use KVS in your app

In layout

Import the KVSProvider from the contexts

// If you are using utils bundle import { KVSProvider } from "@phpcreation/frontend-utils-react-nextjs-bundle/contexts"; // If you copied the context in your app import { KVSProvider } from "@/contexts/KVSContext";

Englobe your app with the KVSProvider

<KVSProvider>{children}</KVSProvider>

In pages where you need to use KVS

Import the useKVS hook from the contexts

// If you are using utils bundle import { useKVS } from "@phpcreation/frontend-utils-react-nextjs-bundle/contexts"; // If you copied the context in your app import { useKVS } from "@/contexts/KVSContext";

Get the function to get specific KVS from the context

const { getSpecificKVS } = useKVS();

This function will return a promise with a KVS (or null if the KVS doesn’t exist)

Use the function to get the KVS you need

const descriptionOptionalKVS = await getSpecificKVS( "phpreaction.project.task_manager.description_minimum" );

How is KVSContext implemented

Variables

interface KVSList { [key: string]: KVS; } const user = useUser(); // You can adapt this to your way to get the user const tenant = useTenant(); // You can adapt this to your way to get the tenant const keyCache = `${tenant}_${user?.id}_KVS`; const [userKVS, setUserKVS] = useState<KVSList>({});
  • user : Will be used to get the user id (User id will be used for caching keys)
  • tenant : To get the tenant for API call and for caching keys
  • keyCache : Will be used as the key to save in localStorage and cache
  • userKVS : Will be the list of user’s KVS

Functions

getUserKVS

This will be used at the beginning in a useEffect to get the list of KVS of the user

  • It’ll get it in this order (If it doesn’t exist in the step, it’ll go to the next one) :
    1. From userKVS state
    2. From localStorage
    3. From cache
    4. From API
  • Then updateKVSList, this will set the KVS :
    • In userKVS state
    • In localStorage
    • In cache
const getUserKVS = async (): Promise<KVSList> => { try { let kvs = userKVS; if (Object.keys(kvs).length > 0) return kvs; kvs = getKVSLocalStorage(); if (Object.keys(kvs).length > 0) { await updateMultipleKVS(kvs); return kvs; } kvs = await getKVSCache(); if (Object.keys(kvs).length > 0) { await updateMultipleKVS(kvs); return kvs; } const { data } = await CallAPI( "GET", tenant, CallAPIURL.keyvaluestore_keyvalues.get + `?user=${user?.id}` ); if (data.response["hydra:totalItems"] === 0) return {}; const userKVSList = data.response["hydra:member"].reduce( (acc: KVSList, kvs: KVS) => { if (kvs) acc[kvs.key] = kvs; return acc; }, {} ); await updateMultipleKVS(userKVSList); return userKVSList; } catch (error) { console.error(error); return {}; } };

getKVSCache

This function will get the list of user’s kvs with the keyCache

const getKVSCache = async () => { const kvsList = await getCachedData(keyCache); setUserKVS(kvsList); return kvsList; };

updateSingleKVS

This function will update with a single KVS: It’ll merge it with the userKVS object then update(updateMultipleKVS) everything

const updateSingleKVS = async (kvs: KVS) => { if (kvs) { const kvsList = { ...userKVS, [kvs?.key]: kvs }; await updateMultipleKVS(kvsList); } };

updateMultipleKVS

This function will update with multiple KVS

const updateMultipleKVS = async (kvsList: KVSList) => { setUserKVS(kvsList); localStorage.setItem(keyCache, JSON.stringify(kvsList)); await storeToCache(keyCache, JSON.stringify(kvsList)); };

getSpecificKVS

This function is the important one, because it’ll be used everywhere.

  • It’ll first get the searched KVS in the userKVS object if it’s inside
  • If not, it’ll then look for that KVS in cache
  • If not, it’ll get it from API then save it in the object and in cache
const getSpecificKVS = async (key: string): Promise<KVS> => { try { // Get in the array let kvs = userKVS[key]; if (kvs) return kvs; // Get in the cache kvs = await getKVSCache(key); if (kvs) return kvs; const { data } = await CallAPI( "GET", tenant, `${CallAPIURL.keyvaluestore_keyvalues.get}?key=${key}` ); kvs = data.response["hydra:member"][0]; if (kvs) { setUserKVS((prev) => ({ ...prev, [kvs.key]: kvs })); await saveKVSCache(kvs); } return kvs; } catch (error) { console.error(error); return null; } };

createKVS

Function to create a KVS

const createKVS = async ( key: string, value: string, clearanceLevel: number = 7 ) => { try { const { data } = await CallAPI( "POST", tenant, CallAPIURL.keyvaluestore_keyvalues.get, "", JSON.stringify({ key, value, user: API_ENDPOINT_VERSION + "/users/" + user?.id, clearanceLevel, }) ); const kvs = data.response["hydra:member"]; if (kvs) { setUserKVS((prev) => ({ ...prev, [kvs.key]: kvs })); await saveKVSCache(kvs); } return data; } catch (err) { console.error(err); } };
Last updated on