Memoize across time & processes. Caches to your local file system, Amazon S3, Google Drive, Google Cloud, PostgreSQL or even BitTorrent 😉
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Build Status

DEPRECATION WARNING: We've decided to deprecate this module in favour of blockai/cloud-cache which works mostly the same except it doesn't try to automatically guess key names (those heuristics were a bit confusing) and clearly separates Promise vs Stream based APIs.

Memoize / cache arbitrary functions to the local file system, Amazon S3, Google Drive, Google Cloud, PostgreSQL, Bittorrent, etc.

Designed to work across different processes or restarts. Functions must be uniquely identified by the user through a name argument. Also uses smart heuristics to further identify like using the package.json's name and version properties.

Supports optional cache expiry through a maxAge argument.

Supports promise and stream returning functions.

Any abstract-blob-store compatible store is supported.



npm install --save persistent-memoize


initMemoize(blobStore [, opts])

Returns a memoize() function.

blobStore should be an abstract-blob-store.

opts (optional) configuration object (defaults to the npm_package_name environment variable which is automatically set when running script through npm)

Used when generating the key used by the blob store. See Key Generation.

opts.version (defaults to the npm_package_version environment variable which is automatically set when running script through npm)

Used when generating the key used by the blob store. We suggest setting the version to 'latest' or null if you chose to version functions individually. This prevents needlessly invalidating the cache when the app version is bumped but the individual function versions haven't changed. See Key Generation.

opts.maxAge (defaults to Infinity)

Specifies after how long (in milliseconds since it was last updated) a cached value is considered stale/expired. Can be overriden through memoize() options.

memoize(fn, name [, opts])

opts.disable (defaults to false)

Can be used to completely disable memoization. This can be useful for debugging.


The function to memoize.

In order for memoization to work as expected, the return value of fn must exclusively depend on its arguments and nothing else. It also must not produce any visible side effects.

From Wikipedia:

A function can only be memoized if it is referentially transparent; that is, only if calling the function has exactly the same effect as replacing that function call with its return value.

In addition, all fn arguments and its return value must be compatible with JSON.stringify.

json-stable-stringify is used to deterministically generate a string from arguments which is then hashed with sha1 to compute a unique key.


Since we want the memoization to work across different processes or restarts, we must name the function so that it can uniquely be identified. See Key Generation.

opts optional configuration object


A version can optionally be specified if you wish to version functions individually instead of using a global version number. See Key Generation.


Overrides the default maxAge value.

Key generation

persistent-memoize is designed to memoize functions across processes and restarts. In order to do that reliably, functions must be named and versioned explicitly.

The algorithm to generate keys for specific function calls is as follows:


Where globalName and globalVersion are respectively the and opts.version values passed to initMemoize(store, opts). name and version are respectively the name argument and opts.version value passed to memoize(fn, name, opts) .

argumentHash is, in pseudo code:

argumentHash = sha1(stringifyToJson(arguments))

Where arguments is an array of arguments passed to the memoized function and stringifyToJson is a call to json-stable-stringify which is similar to JSON.stringify but more deterministic (e.g. order of object keys doesn't matter).


import initMemoize from 'persistent-memoize'
import initBlobStore from 'fs-blob-store'

const memoize = initMemoize(initBlobStore())

const someSlowFunction = (i) => Promise.resolve(`your number is ${i}`)
const getValue = memoize(someSlowFunction, 'someSlowFunction')

  // caches result in blob store and returns it
  .then((str) => console.log(str))
  // now that the result is cached, it will return it from cache
  .then((str) => console.log(str))


import persistentMemoize from 'persistent-memoize'
import fetch from 'node-fetch'
import http from 'http'
import initBlobStore from 's3-blob-store'
import aws from 'aws-sdk'

const store = initBlobStore({
  client: new aws.S3({
    accessKeyId: process.env.S3_ACCESS_KEY,
    secretAccessKey: process.env.S3_SECRET_KEY
  bucket: 'mybucket',

const memoize = persistentMemoize(store)

// Memoize fetch calls (fetch returns a promise)
// Cache will be invalidated when version in `package.json`
// is bumped OR when cache is expired (see maxAge option)
const memoizedFetch = memoize((...args) => (
    .then((response) => {
      // If an error is thrown, the funciton call won't be memoized
      if (!response.ok) throw new Error('oops, problem with request')
      return response.text()
), 'memoizedFetch')

// Functions can be versioned individually
const memoizeNoVersion = persistentMemoize(store, { version: null })

// Sync function. Be careful when memoizing a sync function as it
// doesn't create a drop in replacement because the memoized will
// version be async and returns a promise.
const expensiveComputation = memoizeNoVersion((i) => {
  // some expensive computation :)
  return i + 1
}, 'expensive-computation', { version: 'v1' })

See ./test for more examples.