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

featurehub-store-redis

v1.0.0

Published

FeatureHub Backing Store for Redis

Readme

featurehub-store-redis

Redis-backed feature state store for the FeatureHub JavaScript SDK.

In a multi-instance Node.js deployment (containers, serverless, horizontally-scaled services) each process normally maintains its own in-memory feature state, populated independently from FeatureHub. This plugin bridges that gap: every instance shares a single Redis-cached copy of the feature state, so that updates from FeatureHub are visible across all processes shortly after they arrive on any one of them, without every instance needing its own persistent connection to FeatureHub. Also, if a connection temporarily fails to FeatureHub, as long as Redis is available then updates will keep arriving.

Note This is a standard format across the SDKs that support Redis, all languages will be able to use the same Redis backing store and set of keys.

Node.js only. Requires Node.js ≥ 20.


Installation

npm install featurehub-store-redis
# or
pnpm add featurehub-store-redis

The redis package (v5) and featurehub-javascript-core-sdk are peer dependencies.


Usage

Connect via URL

import { EdgeFeatureHubConfig } from 'featurehub-javascript-node-sdk';
import { RedisSessionStoreUrl } from 'featurehub-store-redis';

const fhConfig = new EdgeFeatureHubConfig(edgeUrl, apiKey);

const store = new RedisSessionStoreUrl('redis://localhost:6379', fhConfig);

await fhConfig.init();

Connect via client options (TLS, auth, etc.)

import { RedisSessionStoreClient } from 'featurehub-store-redis';

const store = new RedisSessionStoreClient(
  {
    socket: { host: 'redis.internal', port: 6380, tls: true },
    password: process.env.REDIS_PASSWORD,
  },
  fhConfig,
);

Connect to a Redis Cluster

import { RedisSessionStoreCluster } from 'featurehub-store-redis';

const store = new RedisSessionStoreCluster(
  {
    rootNodes: [
      { host: 'redis-node-1', port: 6379 },
      { host: 'redis-node-2', port: 6379 },
    ],
  },
  fhConfig, { delay}
);
await store.init();

Call store.close() during graceful shutdown to stop the refresh timer and deregister the listener.


Options

All three classes accept an optional RedisSessionStoreOptions object as their last argument.

| Option | Type | Default | Description | |--------------------|------|----------------|-------------| | prefix | string | "featurehub" | Prefix for all Redis keys written by this store. Change this if multiple environments or applications share the same Redis instance. | | backoffTimeout | number | 500 | Milliseconds to wait between write retries when a WATCH conflict is detected (single-node only). | | retryUpdateCount | number | 10 | Maximum number of write attempts before giving up on a conflicted write. | | refreshTimeout | number | 300 | How often (in seconds) the store polls Redis for changes made by other instances. | |delayInit | boolean| false | Whether to delay callinginit()` or do it in the constructor in the background|

const store = new RedisSessionStoreUrl('redis://localhost:6379', fhConfig, {
  prefix: 'myapp',
  refreshTimeout: 60,   // check for external changes every minute
  backoff_timeout: 200,  // faster retry on conflict
});

Lifecycle

new RedisSessionStoreUrl(url, fhConfig, options)
        │
        ▼
await store.init()   ← connects to Redis, loads cached state into repository,
        │              starts periodic refresh timer. This is done implicitly unless delayInit is `true`.
        │
  [application runs]
        │
store.close()        ← stops refresh timer, deregisters feature listener

store.connected returns true while the store is initialised and the Redis client reports the connection as open.


How it works

The store registers itself as a RawUpdateFeatureListener on the FeatureHub repository. Whenever the repository receives a feature update from FeatureHub (via SSE streaming or polling), the store writes the updated state to Redis. On startup it reads whatever is already in Redis and loads it into the repository immediately, so the first request can be served without waiting for FeatureHub to push an update.

A periodic refresh timer (default every 5 minutes) compares the SHA-256 fingerprint stored in Redis against the last-known fingerprint. If they differ, another instance has written newer state and the local repository is reloaded from Redis.

Optimistic locking

For single-node Redis (RedisSessionStoreUrl and RedisSessionStoreClient), writes use WATCH / MULTI / EXEC to prevent lost updates when multiple instances write concurrently. If the WATCH is invalidated by a competing write, the store backs off and retries (up to 10 times by default). Before each retry it re-reads Redis and merges by keeping the higher-versioned copy of each feature, so no update from any instance is silently dropped.

For Redis Cluster (RedisSessionStoreCluster), WATCH and transactions are not available across cluster slots, so the store falls back to sequential SET calls without optimistic locking.

Server-evaluated keys

The store refuses to initialise when the FeatureHub config uses server-evaluated API keys (keys without a *). Because server-evaluated feature state is computed per-context on the FeatureHub server, caching it in a shared store would cause one user's evaluated flags to bleed into another user's request. Only client-evaluated keys (containing *) are safe to share this way.

Redis key layout

Two keys are written per environment:

| Key | Contents | |-----|----------| | {prefix}_{environmentId} | JSON array of FeatureState objects | | {prefix}_{environmentId}_sha | SHA-256 fingerprint of the above (used for change detection) |

The default prefix is featurehub.


License

MIT