Skip to content

Design Patterns To Use With JavaScript Cookie

Fagner Brack edited this page Mar 10, 2017 · 1 revision

Reference version: 2.1.3

Design Patterns To Use With JavaScript Cookie

Composition over inheritance

More than once people have asked to add new functionality to js-cookie. When we add a new functionality, we're encouraging you to couple your code to it, even if the functionality represents a plug-in mechanism. Instead of doing that, you should be using js-cookie for what it's good for, which is to simplify the access to the browser cookie storage, and the application should be built around it.

Let's assume conceptually that Cookies is a base class where all the methods it supports are documented in the README.

Extending the base class can cause issues because we will have to leave exposed all methods that were inherited from that base class including less common ones like Cookies.getJSON(name), Cookies.getJSON() and Cookies.get(). You might not want those methods exposed for your code, therefore it's more useful to wrap the library in a per use case implementation so that you can build an API that fits the application's domain:

MySpecificPurposeCookies = function() {
  return {
    get: function(name) {
      return Cookies.get(name);
    },
    set: function(name, value, attributes) {
      Cookies.set(name, value, attributes);
    },
    remove: function(name, attributes) {
      Cookies.remove(name, attributes);
    }
  };
};

// In the application code, use it like this and hide unnecessary methods
MySpecificPurposeCookies.get( ... );
MySpecificPurposeCookies.set( ... );
MySpecificPurposeCookies.remove( ... );

This follows the better practice of wrapping vendor libraries you don't control so that if the interface of the library changes, then there's only one place you need to fix in your application code.

Favor decoupling the vendor library from the application code, do not be tied to it

The composition pattern earlier allows the developer to have a much better control of how the library is being used. This is because there's only one place that uses the library directly. If js-cookie API changes, then it's just a matter of changing in a single place, without having to search and replace in the whole application for every occurrence of the global Cookies usage.

Besides, the MySpecificPurpose cookie can be further composed to add additional behavior, such as removing the need to pass the "attributes" when creating it with .set(name, value) or removing it with .remove(name):

MySmartCookieFactory = function() {
  createInstance: function(attributes) {
    return {
      get: function(name) {
        return MySpecificPurposeCookies.get(name);
      },
      set: function(name, value) {
        MySpecificPurposeCookies.set(name, value, attributes);
      },
      remove: function(name) {
        MySpecificPurposeCookies.remove(name, attributes);
      }
    }
  }
};

// Now we can have a specific instance with a clean API
MyGoogleCookies = MySmartCookieFactory.createInstance({
  domain: 'google.com'
});
MyGoogleCookies.set(name, value); // Will set passing the "domain: google.com" attribute
MyGoogleCookies.remove(name); // Will remove passing the "domain: google.com" attribute

Avoid depending on vendor namespaces to prevent potential naming clashes

Using application-specific code to wrap the library has another benefit, which is preventing name clashes.

Instead of using the global window.Cookies, we can just use the Cookies.noConflict() to remove the global reference and delegate the behavior to our application internals:

MySpecificPurposeCookies = function() {
  InternalCookies = Cookies.noConflict();
  return {
    get: function(name) {
      return InternalCookies.get(name);
    },
    set: function(name, value, attributes) {
      InternalCookies.set(name, value, attributes);
    },
    remove: function(name, attributes) {
      InternalCookies.remove(name, attributes);
    }
  };
};

// In the application code, use it like this and hide unnecessary methods
MySpecificPurposeCookies.get( ... );
MySpecificPurposeCookies.set( ... );
MySpecificPurposeCookies.remove( ... );

// Global is not cluttered
window.Cookies; // undefined