Article thumbnail

🌟 Working with local storage vs session storage

6m
patterns
facade
storage

Intro

How to work with local and session storage efficiently and in a scalable way. Let's create some facades to unify the maintenance of these!

Prelude

Local storage and session storage are the most commonly used mechanisms for storing data in web applications. I skip cookies 🍪 because I'm allergic to gluten.

Today we'll create a mechanism that will unify the work of these impostors, ensuring type safety and making reading and much manipulation simpler!

Why do we need a wrapper for local/session storage?

Analyzing the local/session storage API, you can see that their methods are identical - they have the same signature.

Loading

What catches the eye at first glance?

  • The key is 'string'
  • The received value is of type 'string'
  • The APIs are identical
  • No encapsulation

It's easy to make a typo...

Loading

In addition, we can by mistake run this code on the server side, which has no chance of working - both APIs are only available in the browser environment.

Loading

The error message may not necessarily guide you to the cause of the error. This is a problem, especially for beginners.

What about reading and writing?

We must do repetitive logic every time - parsing when reading and writing.

Loading

It's time for one last treat! What about mocking when testing? Well, we are facing the same duplication of logic and lack of strong typing.

Loading

As you can see there is a lot of it, and I still left out such things as:

  • What if we need an additional method?
  • No encapsulation
  • Singleton on the entire application - easy to mess up other functionality
  • We can create a collision - overwrite a value set in another functionality

We need to get it right!

A little inspiration to get started

Do you remember the Axios library? You probably do! In it, we create an instance of an object that, based on the passed configuration, returns you a specific, consistent and convenient to use API. We want to achieve the same result.

At Axios, we do just that:

Loading

Our small library will do just that:

Loading

Here are the methods calls:

Loading

Take a look at the gif below and see what the target idea is:

Loading

TypeScript will look out for us accordingly

Let's model local/session storage and write tests

To begin with, let's create a string literal type in which we define the supported data sources and let's add a storage function skeleton.

Loading

Now tests for an implementation that is not ready yet. The tests will be red until we write code to make them green - we will use TDD.

If you want to understand what test-driven development is and want to explore the secrets of testing, then I invite you to this article and the following course.

Loading

Loading

Loading

Loading

Well, and at the very end, a test that verifies the operation of several methods called after each other - as it will be done in real life.

Loading

Time for implementation - we make our tests green

Well, we have the models, the tests and we know how our API will look/work - now it's time for implementation which is what FE devs like us love the most.

We will start with a function that will throw an exception with an appropriate message if local/session storage is not defined in a global object - for example, we ran our function on the server side.

Loading

Next, we will add a method implementation for reading a single value: get. Note the casting that is necessary - the JSON.parse method does not allow you to pass a generic parameter.

Loading

It's time for getAll, which is designed to return all values set. Note the Record and its "strange" typing. We want the returned object to have the same keys as the passed interface and the corresponding values.

Loading

At this point, we have two tests that are green. This means that throwing exceptions and reading a single value and multiple values is working properly.

  • throws an error if any storage is undefined ✔️
  • allows to get all values ✔️

Now it's time to getKeys. Here there is no surprise. We simply return the keys, which we can access thanks to closure.

Loading

Why is it a function and not simply keys: keys. It must be a function that reads the "latest" values. If it was a variable - the value would be always the same - initially adjusted keys (empty array).

After this change, the next test becomes green.

  • picks keys from local storage or session storage ✔️

Now it's time for the last four methods: remove, set, patch and clear.

Loading

In remove, we remove the value from the selected local/session storage, and then we get rid of the passed key from the array.

In set, we set the value and add the key. At the same time, we check if the key already exists. If it does, we do not add the same - why do we need duplicates?

In clear, we clean everything that has ever been set - not all local/session storage - this could negatively affect other functionality.

In the patch, we add as many values as we passed keys to the object. At the same time, we check if the passed value is by any chance undefined - this will cause an exception in JSON.parse when trying to read it, so we're skipping these values.

After all these code changes, our tests are as green as Shrek.

Complete example

If you want the final code you can get it in the following repository. Below you have the full solution displayed via code snippets.

Loading

Loading

Loading

Summary

Once again at ease with what we have achieved 🧂:

  • We have developer-friendly exceptions
  • Local/session storage management is modular
  • We have type-safety and typo protection
  • No need to store keys in variables
  • We can easily add new methods and extend the solution
  • The solution can be used to mock up values in tests as well
  • Consistent API
  • Less risk of collisions or overwriting values in other features

It is worth mentioning that introducing an abstraction such as so does not always make sense. In this case, it is useful, but it is worth conducting a comparison after the work is completed what is the profit and the result. Maybe it was not needed?

In my opinion, it is not essential, but it makes life easier.

I create content regularly!

I hope you found my post interesting. If so, maybe you'll take a peek at my LinkedIn, where I publish posts daily.

Comments

Add your honest opinion about this article and help us improve the content.

created: 10-07-2023
updated: 16-10-2023