-
-
Notifications
You must be signed in to change notification settings - Fork 27
feat: Add Store.redis() for standard Redis/ioredis/Valkey clients #208
Description
Problem
Store.upstash() works because the Upstash REST SDK auto-serializes JSON (including BigInt) on their end. Standard Redis clients (ioredis, node-redis, Valkey) only accept strings, so passing channel state with BigInt fields directly to client.set() either throws or corrupts data.
Anyone running self-hosted Redis/Valkey (common in Kubernetes deployments) has to write a custom store adapter with BigInt-safe serialization:
const BIGINT_SUFFIX = "#__bigint";
function createRedisStore(redis: IoRedis): Store {
return Store.from({
async get(key) {
const raw = await redis.get(key);
if (raw == null) return null;
return JSON.parse(raw, (_, v) =>
typeof v === "string" && v.endsWith(BIGINT_SUFFIX)
? BigInt(v.slice(0, -BIGINT_SUFFIX.length))
: v,
);
},
async put(key, value) {
await redis.set(
key,
JSON.stringify(value, (_, v) =>
typeof v === "bigint" ? \`\${v}\${BIGINT_SUFFIX}\` : v,
),
);
},
async delete(key) {
await redis.del(key);
},
});
}This is error-prone and every self-hosted user will rediscover the BigInt serialization issue independently.
Proposal
Add Store.redis() that accepts any client with standard get/set/del string methods and handles #__bigint serialization internally (using ox's Json.parse/Json.stringify, same as Store.cloudflare() and Store.memory() already do):
import { Store } from "mppx/server";
import Redis from "ioredis";
const redis = new Redis("redis://localhost:6379");
const store = Store.redis(redis);
// Works with any client that has get/set/del string methods:
// - ioredis
// - node-redis (@redis/client)
// - ValkeyWhy not extend Store.upstash()?
upstashis named after a specific product - using it for generic Redis would be confusing- The serialization behavior is fundamentally different (Upstash auto-serializes, standard clients don't)
- A separate
Store.redis()is additive with no breaking changes
Interface
export function redis(client: redis.Parameters): Store
export declare namespace redis {
type Parameters = {
get: (key: string) => Promise<string | null>
set: (key: string, value: string) => Promise<unknown>
del: (key: string) => Promise<unknown>
}
}This covers ioredis, node-redis, and any Redis-compatible client without requiring a specific package dependency.