2.9 KiB
2.9 KiB
redis-store
Purpose
Provides a Redis-backed IdempotencyStoreAdapter<T> with automatic key expiration using Redis native TTL. By accepting a generic RedisClientAdapter interface rather than a concrete library, this module integrates with ioredis, redis, Upstash Redis, or any compatible client without coupling to a specific driver.
Features
- JSON-serialised records stored as Redis string values
- Automatic TTL set on every
set()call based onrecord.expiresAt - Fallback TTL (
defaultTtlSeconds) for records whose expiry is in the past - Configurable key prefix for namespace isolation (multi-tenant)
cleanup()scans expired records server-side for manual purging- Generic
<T>type parameter for typed payloads
Usage: Basic Example
import { RedisIdempotencyStore } from "@suaveplan/idempotency/store";
import type { RedisClientAdapter } from "@suaveplan/idempotency/store";
declare const redisAdapter: RedisClientAdapter; // your ioredis wrapper
const store = new RedisIdempotencyStore({
client: redisAdapter,
prefix: "app:idempotency:",
});
Usage: Advanced Example
import { RedisIdempotencyStore } from "@suaveplan/idempotency/store";
const store = new RedisIdempotencyStore({
client: redisAdapter,
prefix: `tenant:${tenantId}:idempotency:`,
defaultTtlSeconds: 3600, // 1 hour fallback
});
// Manual cleanup (most records expire via Redis TTL automatically)
const removed = await store.cleanup(Date.now());
console.log(`Manually removed ${removed} stale records`);
API Reference
new RedisIdempotencyStore<T>(config: RedisIdempotencyStoreConfig)
| Option | Type | Default | Description |
|---|---|---|---|
client |
RedisClientAdapter |
required | Redis client adapter |
prefix |
string |
"idempotency:" |
Key namespace prefix |
defaultTtlSeconds |
number |
86400 |
Fallback TTL when expiresAt is past |
get(key): Promise<IdempotencyRecord<T> | undefined>
Fetches and JSON-parses the record. Returns undefined if not found (key expired or never set).
set(key, record): Promise<void>
Serialises the record as JSON and stores it with a TTL computed from record.expiresAt - Date.now().
delete(key): Promise<boolean>
Calls DEL on the prefixed key; returns true if a key was removed.
cleanup(beforeTimestamp): Promise<number>
Scans all keys under the prefix, parses each, and deletes those with expiresAt < beforeTimestamp. Returns the count removed.
Implementation Notes
- Redis TTL handles the common expiration case automatically;
cleanupis only needed for records that were not expired by Redis (e.g. records with very long TTLs where early manual deletion is desired). scanis used rather thanKEYSto avoid blocking the Redis event loop in production.- JSON serialisation assumes records are fully serialisable;
Dateobjects should be stored as timestamps (numbers).