Choosing a JavaScript Cache Library — Fall 2019 Edition

Callback Insanity
6 min readOct 29, 2019
Photo by Becca McHaffie on Unsplash

If you find this article useful make sure to like it, or if you have any suggestions just mention Callback Insanity .

Table of Contents

Preface
Criteria
Comparison Tables
— — Back-end
— — Isomorphic
— — Front-end
NPM search tags

Choosing a JavaScript library in 2019 feels like somewhat similar to buying a car. Once you choose one you’re kinda stuck with it for the next couple of years, so choose wisely…
~ Totally not-made-up quote

Preface — The Good Ole Drupal days

Coming from Drupal 6.x and 7.x, I am used to having a simple, consistent interface for persistent storage.

The main two persistent cache functions in Drupal, cache_get() and cache_set() , allowed you to store an item with a time-to-live (TTL), and the data would automatically get serialized and invalidated from the cache:

cache_set('cached-item-name', $data, 'cache-container', timestamp)

Minimizing the amount of caching code written made the application easier to maintain and less prone to bugs, while providing a common interface for a large community of developers.

In JavaScript world, browsers currently support a well-documented, mature interface for persisting storage via a variety of APIs, of which localStorage is part of. However these are missing some of the extra functionality that made cache management in Drupal so effective, such as cache invalidation.

I am an advocate of proudly invented elsewhere, so in this post I am going to compile a list of modern NPM projects that facilitate managing cache in JavaScript with the elegance that Drupal used to do since 2005.

I compiled this list by almost strictly by searching different tags in the NPM library and sorting the results by popularity and other parameters. You can see some of those tags here.

Criteria

I’m in the process of creating an Electron app for fun, which will be making calls to third-party APIs. These APIs require authentication with tokens, which need to be retrieved, stored, then refreshed when expired.

The main 3 criteria that I am looking for is: persistence, cache invalidation, and a simple get/set interface.

Here’s a more detailed list of non-optional criteria:

  • Simple interface — It shouldn’t take more than 3 lines of code to include the library, get(item) a cache item, and set(item, ttl) a cache item.
  • Cache eviction — Supports automatic cache object expiration/eviction based on a time-to-live (TTL) value. Preferably set on each individual item, but I’m flexible if the TTL is global.
  • Persistent— Cached information survives between application restarts.

And here’s a list of the optional, but preferred criteria:

  • Serialization — Does the project support any other data other than text, and if it does, does it automatically serialize before storage?
  • Popularity — Is the project widely known and adopted ?
  • Maintenance — Was the project updated recently on Github ?
  • Promises — Because 2012 called me this morning asking for it’s callbacks back. No, seriously!
  • Stand-alone — Caching needs to work without the need to communicate with a third-party service or process. I’m not paying Amazon a dime to host anything, compiling SQLite for different platforms, or asking users to install anything other than the app.

In addition, the JavaScript caching library can be executed from three places:

Front-end libraries: mainly use cookies, localStorage, IndexDB, or memory. They main benefit is that all storage happens in the browser, and there is no need for communication between the front- and back- ends to get or set cache items.

Back-end libraries: They run in Node.js, and unlike a browser, are the best for storing cache information into the filesystem, as well as any other dedicated cache (Memcache, Redis) or SQL/No-SQL (MySQL/MongoDB) service. The downside is that the front-end needs IPC to communicate with the back-end.

Isomorphic libraries: These projects can compile for and run in both the front- and back-end — although their functionality may vary depending on the environment. You can read more about Isomorphism here.

Both front- and back-end libraries have the ability to persist data between application cycles. However, based on the NPM download numbers and Github commit stats, Node.js cache libraries seem to be more popular ? Maybe CDNs are impacting front-end library usage statistics ?

Comparison Tables

Updated October 29, 2019

Table Legend:

  • Name: Name of the NPM library
  • TTL: Time-to-live — supports cache eviction/expiration out-of-the-box
  • Res: Restart — will survive application or OS restarts
  • d/wk: Downloads per week on NPM.
  • Git: When was the latest stable release made available on Github.
  • Size: Approximate size of library, minified but not compressed. Sizes are based on estimates provided by bundlephobia.
  • Data: Does it store plain text only, or does it serialize multiple data types ?

Back-end libraries

Name          | TTL  | Res | d/wk | Git  | Promises | Size  | 
----------- | ---- | --- | ---- | ---- | -------- | ----- |
cacache | no | yes | 11M | 2019 | yes | 144kB |
node-persist | yes | yes | 20k | 2019 | yes | 8kB |
keyv | yes | no | 2.5M | 2019 | yes | 2.5kB |
keyv-file | yes | yes | 300 | 2019 | yes | 43kB |
keyv-lru | yes | no | 400 | 2019 | ? | 5kB |
cache-manager | yes | yes | 280k | 2019 | yes | 34kB |
cache-all | yes | yes | 180 | 2019 | yes | 118kB |
cacheman | yes | yes | 8k | 2015 | callback | 6.7kB |
tlru | yes | no | 500 | 2019 | no | 2.8kB |
cachd | yes | no | 50 | 2017 | events | error |

Isomorphic libraries

Name                 | TTL  | Res | d/wk | Git  | Promises | Size  | 
-------------------- | ---- | --- | ---- | ---- | -------- | ----- |
js-cache | yes | no | 8k | 2017 | no | 5kB |
tiny-lru | yes* | no | 87k | 2019 | no | 1.5kB |
lokijs | no | yes | 200k | 2019 | yes* | 73kB |
ttl-localstorage | yes | yes | 157 | 2019 | yes | 81kB |
localstorage-ttl | yes | yes | 400 | 2016 | no | 496B |
level | no | yes | 62k | 2019 | cb+event | 69kB |
level-ttl | yes | yes | 141 | 2016 | no | 5.2kB |
@thi.ng/cache | yes | no | 800 | 2019 | no | 10kB |
@brokerloop/ttlcache | yes | no | 100 | 2019 | events | 2.9kB |

Front-end libraries

Name        | TTL  | Res | d/wk | Git  | Promises | Size  |
----------- | ---- | --- | ---- | ---- | -------- | ----- |
store.js | yes* | yes*| 125k | 2017 | no | 10kB |
cache-base | no | no | 8k | 2017 | no | 5kB |
dexie | N/A | yes | 33k | 2019 | yes | 55kB |
localforage | no | yes | 257k | 2018 | yes | 28kB |
idb | no | yes | 300k | 2019 | yes | 1kB |
in-memoriam | yes | no | 800 | 2019 | no | 1.4kB |

Utilities

These modules are not strictly key/value stores, but used along with an existing k/v store, they could add some additional functionality, such as date parsing or object invalidation:

NPM Index keywords

If interested, here are some suggested keywords you can use to search the NPM index for storage modules:

cache, caching, store, storage, localStorage, sessionstorage, indexeddb, expire, ttl

--

--

Callback Insanity

Organic, fair-sourced DevOps and Full-Stack things. This is a BYOB Establishment — Bring Your Own hipster Beard.