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

secure-store-redis

v4.1.3

Published

A simple wrapper to encrypt and decrypt data stored in Redis

Downloads

1,202

Readme

secure-store-redis

npm version license build

Encrypt and store data in Redis. Uses AES-256-GCM encryption with unique IVs per entry.

Installation

npm install secure-store-redis

Quick Start

import SecureStore, { SecretValidator } from "secure-store-redis";

const store = new SecureStore({
    uid: "myApp",
    secret: SecretValidator.generate(),
    redis: { url: "redis://localhost:6379" },
});

await store.connect();
await store.save("key", { foo: "bar" });
const data = await store.get("key");
await store.disconnect();

API

Constructor

new SecureStore(config: SecureStoreConfig)

| Option | Type | Required | Description | | ------------------ | ------------------------------- | -------- | ----------------------------------------------------------------- | | uid | string | Yes | Unique prefix for Redis keys (e.g., "myApp", "myApp:sessions") | | secret | string | Yes | 32-character encryption secret. Use SecretValidator.generate(). | | redis | RedisOptions | { url: string } | { client: Redis | Cluster } | Yes | Redis connection config or existing client | | allowWeakSecrets | boolean | No | Bypass secret strength validation (default: false) |

Using an Existing Redis Client

You can pass your own ioredis Redis or Cluster client instead of connection options. This enables connection sharing, pre-configured clients, and integration with existing Redis infrastructure.

import { Redis } from "ioredis";
import SecureStore, { SecretValidator } from "secure-store-redis";

const redis = new Redis({ host: "localhost", port: 6379 });

const store = new SecureStore({
    uid: "myApp",
    secret: SecretValidator.generate(),
    redis: { client: redis },
});

await store.connect();
await store.save("key", "value");
await store.disconnect();

// Your client is still connected - you manage its lifecycle
console.log(redis.status); // "ready"
await redis.quit();

Redis Cluster

import { Cluster } from "ioredis";
import SecureStore, { SecretValidator } from "secure-store-redis";

const cluster = new Cluster([
    { host: "node1", port: 6379 },
    { host: "node2", port: 6379 },
]);

const store = new SecureStore({
    uid: "myApp",
    secret: SecretValidator.generate(),
    redis: { client: cluster },
});

await store.connect();

Sharing Connections

Multiple SecureStore instances can share a single Redis connection:

const redis = new Redis();

const sessionsStore = new SecureStore({
    uid: "sessions",
    secret: sessionSecret,
    redis: { client: redis },
});

const cacheStore = new SecureStore({
    uid: "cache",
    secret: cacheSecret,
    redis: { client: redis },
});

await sessionsStore.connect();
await cacheStore.connect();

// Both stores use the same connection
// Disconnecting either store does NOT close the Redis client

Important notes:

  • When using an external client, disconnect() will NOT close the Redis connection - you are responsible for calling redis.quit() when done
  • The client can be in any connectable state (ready, connecting, or lazyConnect); connect() will wait for it to be ready
  • If the client is already closed, connect() will throw a ConnectionError

Methods

connect(): Promise<void>

Connect to Redis. Must be called before other operations.

save<T>(key: string, data: T, postfix?: string): Promise<void>

Encrypt and store data.

get<T>(key: string, postfix?: string): Promise<T | null>

Retrieve and decrypt data. Returns null if not found or decryption fails.

delete(key: string, postfix?: string): Promise<number>

Delete data. Returns count of deleted keys.

disconnect(client?: Redis): Promise<void>

Close Redis connection. Optionally pass a specific Redis client to disconnect.

namespace<TSchema>(name: string): TypedNamespace<TSchema>

Create a typed namespace for organizing data. See Namespaces for details.

Properties

  • client: RedisClient | undefined - The underlying ioredis client (Redis or Cluster)
  • isConnected: boolean - Connection status

Namespaces

Namespaces allow you to organize data with type-safe operations. Each namespace acts as an isolated partition within the same store.

const store = new SecureStore({
    uid: "myApp",
    secret: SecretValidator.generate(),
    redis: { url: "redis://localhost:6379" },
});
await store.connect();

// Create a typed namespace
interface UserSchema {
    profile: { name: string; age: number };
    settings: { theme: string; notifications: boolean };
}

const users = store.namespace<UserSchema>("users");

// Type-safe operations
await users.save("profile", { name: "John", age: 30 });
await users.save("settings", { theme: "dark", notifications: true });

const profile = await users.get("profile"); // { name: string; age: number } | null
const settings = await users.get("settings");

await users.delete("profile");

Namespace Methods

Each namespace provides the same core operations as the store:

  • get<K>(key: K): Promise<T[K] | null>
  • save<K>(key: K, data: T[K]): Promise<void>
  • delete<K>(key: K): Promise<number>

Namespace Isolation

Data in different namespaces is completely isolated:

const users = store.namespace("users");
const sessions = store.namespace("sessions");

await users.save("data", "user-data");
await sessions.save("data", "session-data");

await users.get("data");    // "user-data"
await sessions.get("data"); // "session-data"
await store.get("data");    // null (root store is separate)

SecretValidator

Utility class for generating and validating encryption secrets.

Methods

generate(length?: number): string

Generate a cryptographically secure secret. Defaults to 32 characters if no length specified.

validate(secret: string): { valid: boolean; reason?: string }

Validate secret strength against security requirements.

Validation Rules

Secrets must:

  • Be exactly 32 characters
  • Have sufficient entropy (Shannon entropy ≥ 4.0)
  • Not contain weak patterns (repeated chars, sequential numbers, etc.)
  • Contain at least 3 of: uppercase, lowercase, numbers, special characters

Error Handling

All errors extend SecureStoreError and include a code property for programmatic handling.

import {
    SecureStoreError,
    ConnectionError,
    EncryptionError,
    ValidationError,
} from "secure-store-redis";

try {
    await store.connect();
} catch (err) {
    if (err instanceof ConnectionError) {
        console.error(`Connection failed (${err.code}):`, err.message);
        // err.code === "CONNECTION_ERROR"
    }
}

| Error Class | Code | When Thrown | | ----------------- | -------------------- | ------------------------------------ | | ConnectionError | CONNECTION_ERROR | Redis connection failures | | EncryptionError | ENCRYPTION_ERROR | Encryption/decryption failures | | ValidationError | VALIDATION_ERROR | Invalid configuration, keys, or data |

Security Best Practices

  • Store secrets in environment variables, not code
  • Use SecretValidator.generate() for cryptographically secure secrets
  • Enable Redis authentication and TLS in production
  • Use unique uid values per application/environment to prevent data collisions
  • Rotate secrets periodically (requires re-encryption of existing data)

Migration from v3.x

Breaking Changes

| Change | Migration | | -------------------- | ------------------------------------------ | | uid required | Add explicit uid to constructor | | secret required | Use SecretValidator.generate() or provide your own | | connect() required | Call await store.connect() before use | | AES-256-GCM | Re-encrypt existing data (format changed) | | Secret validation | Use strong secrets or set allowWeakSecrets: true |

Example

// v3.x
const store = new SecureStore({ redis: { url: "..." } });
await store.init();

// v4.0
const store = new SecureStore({
    uid: "myApp",
    secret: SecretValidator.generate(),
    redis: { url: "..." },
});
await store.connect();

TypeScript

Full TypeScript support with exported types:

import SecureStore, {
    SecureStoreConfig,
    TypedNamespace,
    RedisClient,
    SecretValidator,
    SecureStoreError,
    ConnectionError,
    EncryptionError,
    ValidationError,
} from "secure-store-redis";

License

MIT