Redis State Store
@purista/redis-state-store stores service state as JSON strings in Redis. It is the most common production choice: Redis is fast, well-understood operationally, and adds TTL-based expiry and pub/sub on top of simple key-value semantics.
Capabilities
| Feature | Support |
|---|---|
Read (getState) | ✅ (enabled by default) |
Write (setState) | ✅ (opt-in) |
Delete (removeState) | ✅ (opt-in) |
| TTL / automatic expiry | ✅ (via Redis EXPIRE) |
| Persistence across restarts | ✅ (Redis persistence) |
| Pub/sub for reactivity | ✅ (Redis-native) |
| Cluster / Sentinel | ✅ (node-redis) |
Install
npm install @purista/redis-state-store
Setup
import { RedisStateStore } from '@purista/redis-state-store'
const stateStore = new RedisStateStore({
config: {
url: process.env.REDIS_URL ?? 'redis://localhost:6379',
},
enableSet: true,
enableRemove: true,
})
const myService = await myV1Service.getInstance(eventBridge, { stateStore })
The config object is passed directly to node-redis — TLS, authentication, cluster, and Sentinel options are all supported.
Usage inside a handler
.setCommandFunction(async function (context, payload) {
// Track last-processed position
await context.states.setState('lastProcessedAt', new Date().toISOString())
const { lastProcessedAt } = await context.states.getState('lastProcessedAt')
// Session state
await context.states.setState(`session:${payload.userId}`, {
cart: payload.cartItems,
updatedAt: Date.now(),
})
// Clean up
await context.states.removeState(`session:${payload.userId}`)
})
Common patterns
Rate limiting:
const key = `ratelimit:${context.userId}`
const { count } = await context.states.getState(key) ?? { count: 0 }
if (count >= 100) throw new HandledError(StatusCode.TooManyRequests, 'rate limit exceeded')
await context.states.setState(key, { count: count + 1 })
// TTL must be set directly via the Redis client if needed
Job status tracking:
await context.states.setState(`job:${jobId}`, { status: 'processing', startedAt: Date.now() })
// ... do work ...
await context.states.setState(`job:${jobId}`, { status: 'done', completedAt: Date.now() })
Operational tips
- Use a consistent key prefix per service (
user-service:state:) to avoid cross-service collisions - Enable Redis persistence (RDB snapshots or AOF) so state survives Redis restarts
- Use Redis Sentinel or Cluster for HA in production
- Monitor Redis memory usage — unbounded state writes without expiry or cleanup policies will grow indefinitely