npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chriscdn/memoize-redis

v1.0.0

Published

Memoize asynchronous functions in Redis.

Readme

@chriscdn/memoize-redis

A utility for memoizing asynchronous functions using Redis hashes.

It provides:

  • Redis backed caching
  • Per entry TTLs
  • Custom cache key resolution
  • Conditional caching via ttl
  • In-flight request deduplication
  • Cache inspection and deletion
  • Explicit cache refresh, including background refresh
  • Graceful operation when Redis is unavailable

Installation

npm install @chriscdn/memoize-redis

The package has a peer dependency on redis package v6 or greater, so be sure to install that as well.

Basic usage

Create a memoizer from an existing Redis client:

import { createClient } from "redis";
import {
  createRedisMemoizer,
  createRedisMemoizerNoHash,
} from "@chriscdn/memoize-redis";

const redisClient = createClient({
  url: process.env.REDIS_URL,
});

// Attach an error listener before connecting. The redis client emits

// an "error" event on connection issues, and Node will crash on an

// unhandled "error" event if no listener is registered.

redisClient.on("error", (error) => {
  console.error("Redis error", error);
});

await redisClient.connect();

const { MemoizeRedis } = createRedisMemoizer(redisClient);

const getUser = MemoizeRedis(
  async (id: string) => {
    return fetchUserFromDatabase(id);
  },
  {
    redisKey: "users",
    ttl: ({ key, value, args }) => 60_000,
  },
);

const user = await getUser("123");

Use createRedisMemoizerNoHash if using a version of Redis prior to 7.4, which does not support the required hash commands.

Options

redisKey (required)

The Redis hash used to store the cached values.

{
  redisKey: "users";
}

ttl (required)

A function that determines how long the returned value should remain cached.

The value is specified in milliseconds.

{
  ttl: ({ key, value, args }) => 60_000;
}

The callback receives the resolved value and cache key. Returning 0 or a negative value prevents the value from being cached.

This also makes it possible to calculate the TTL from the returned value:

{
  ttl: ({ key, value, args }) => value.expiresAt - Date.now();
}

resolver (optional)

By default, the cache key is generated by converting the function arguments to JSON using canonicalize and hashing the result with SHA 256.

A custom resolver can be useful when some arguments are irrelevant to the cache key, or when an argument contains a value that cannot be safely serialized.

This behaviour can be customized by providing a resolver callback:

resolver: (...args) => `MyCacheKey::${args[0]}`,

The resolver must return a string, which is used as the cache key.

refreshWhen (optional)

A function that determines whether a cached value should be refreshed in the background.

The callback receives the remaining TTL in milliseconds and the cached value.

{
  refreshWhen: ({ttl, value, args}) => ttl < 10_000,
}

If the callback returns true, the underlying function is executed in the background.

The existing cached value continues to be returned until the refresh completes, so callers do not wait for the underlying function.

Only one refresh is executed at a time for each cache key. A refresh already in progress is reused by subsequent refresh attempts.

The refresh is best effort. Errors from the background refresh are ignored.

Cache behavior

  • When the memoized function is called, it first checks Redis.
  • If a cached value exists, it is deserialized and returned without calling the original function.
  • If there is no cached value, the original function is called and its result may be cached according to ttl. Return 0 (or a negative number) from ttl to skip caching for that value.
  • If Redis is unavailable, the original function is called normally and the result is returned without caching.
  • If the resolved value is undefined, it is not cached. A value of null is cached normally.

In-flight request deduplication

Concurrent requests for the same cache key are deduplicated within the current process.

For example:

const [a, b, c] = await Promise.all([
  getUser("123"),
  getUser("123"),
  getUser("123"),
]);

If "123" is not already cached, the underlying function is only executed once.

All three callers receive the same resulting promise.

This deduplication is local to the current process. It does not provide distributed request locking between multiple application instances.

Methods

The memoized function provides several additional methods.

clear()

Delete the Redis hash.

await getUser.clear();

has(...args)

Checks whether a cache entry exists.

const exists = await getUser.has("123");

Returns true or false accordingly.

Returns null if Redis is unavailable or a Redis error occurs.

delete(...args)

Deletes a single cache entry.

await getUser.delete("123");

The operation can be queued by the Redis client if Redis is unavailable. The returned promise can be awaited to wait for completion, or the method can be called without await for a background operation.

ttl(...args)

Returns the remaining TTL for a cache entry in milliseconds.

const remaining = await getUser.ttl("123");

The return values are:

| Value | Meaning | | ----- | -------------------------------------------------------------------------- | | >=0 | entry TTL in milliseconds | | -1 | entry exists without an expiration (should never happen with this package) | | -2 | entry does not exist | | -3 | Redis is unavailable or a Redis error occurred |

refresh(...args)

Forces the underlying function to execute instead of using the existing cached value.

await getUser.refresh("123");

The resulting value is processed using the normal caching rules, including ttl.

If another refresh or cache miss for the same key is already in progress, the existing in-flight request is reused.

This can also be called without await to do a background refresh.

Detecting memoized functions

The package exports isMemoizedAsyncRedis:

import { isMemoizedAsyncRedis } from "@chriscdn/memoize-redis";

if (isMemoizedAsyncRedis(value)) {
  await value.has(key, field);
}

This can be useful when working with dynamically configured functions.

Redis availability

The memoized function is best effort when Redis is unavailable.

When Redis is unavailable:

  • Cache reads are treated as cache misses
  • Cache writes are skipped
  • The underlying function still executes
  • The underlying function's result is returned normally

The methods clear and delete are sent to Redis even when the client is offline and can be queued by the Redis client until the connection is restored.

The methods has and ttl return null or -3 respectively when Redis is unavailable or a Redis error occurs.

The Redis client itself is responsible for establishing and maintaining its connection, including registering an "error" listener as shown in Basic usage.

Serialization

Cached values are serialized using JSON.stringify and restored using JSON.parse.

Consequently, values should be JSON serializable.

Types such as Date, Map, Set, BigInt, class instances, and undefined do not preserve their original JavaScript representation through this serialization process.

If a cached value contains invalid JSON, the entry is removed from Redis and the underlying function is executed again.

License

MIT