/redis-patterns
Redis patterns including caching strategies, pub/sub, streams for event processing, Lua scripts, and data structures
$ npx -y skills add rohitg00/awesome-claude-code-toolkit --skill redis-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/redis-patterns
Context preview
The summary Claude sees to decide when to auto-load this skill.
Redis patterns including caching strategies, pub/sub, streams for event processing, Lua scripts, and data structures
SKILL.md
redis-patterns.SKILL.mdname: redis-patterns
description: Redis patterns including caching strategies, pub/sub, streams for event processing, Lua scripts, and data structures
Redis Patterns
Caching Strategies
async function getUser(userId: string): Promise<User> {
const cacheKey = `user:${userId}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const user = await db.user.findUnique({ where: { id: userId } });
if (user) {
await redis.set(cacheKey, JSON.stringify(user), "EX", 3600);
}
return user;
}
async function invalidateUser(userId: string): Promise<void> {
await redis.del(`user:${userId}`);
await redis.del(`user:${userId}:orders`);
}
async function cacheAside<T>(
key: string,
ttlSeconds: number,
fetcher: () => Promise<T>
): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const value = await fetcher();
await redis.set(key, JSON.stringify(value), "EX", ttlSeconds);
return value;
}Rate Limiting with Sliding Window
async function isRateLimited(
clientId: string,
limit: number,
windowSeconds: number
): Promise<boolean> {
const key = `ratelimit:${clientId}`;
const now = Date.now();
const windowStart = now - windowSeconds * 1000;
const pipe = redis.multi();
pipe.zremrangebyscore(key, 0, windowStart);
pipe.zadd(key, now, `${now}:${crypto.randomUUID()}`);
pipe.zcard(key);
pipe.expire(key, windowSeconds);
const results = await pipe.exec();
const count = results[2][1] as number;
return count > limit;
}Pub/Sub
const subscriber = redis.duplicate();
await subscriber.subscribe("notifications", "orders");
subscriber.on("message", (channel, message) => {
const event = JSON.parse(message);
switch (channel) {
case "notifications":
handleNotification(event);
break;
case "orders":
handleOrderEvent(event);
break;
}
});
async function publishEvent(channel: string, event: object): Promise<void> {
await redis.publish(channel, JSON.stringify(event));
}Streams for Event Processing
async function produceEvent(stream: string, event: Record<string, string>) {
await redis.xadd(stream, "*", ...Object.entries(event).flat());
}
async function consumeEvents(
stream: string,
group: string,
consumer: string
) {
try {
await redis.xgroup("CREATE", stream, group, "0", "MKSTREAM");
} catch {
// group already exists
}
while (true) {
const results = await redis.xreadgroup(
"GROUP", group, consumer,
"COUNT", 10,
"BLOCK", 5000,
"STREAMS", stream, ">"
);
if (!results) continue;
for (const [, messages] of results) {
for (const [id, fields] of messages) {
await processMessage(fields);
await redis.xack(stream, group, id);
}
}
}
}Streams provide durable, consumer-group-based event processing with acknowledgment and replay.
Lua Script for Atomic Operations
const acquireLock = `
local key = KEYS[1]
local token = ARGV[1]
local ttl = ARGV[2]
if redis.call("SET", key, token, "NX", "EX", ttl) then
return 1
end
return 0
`;
const releaseLock = `
local key = KEYS[1]
local token = ARGV[1]
if redis.call("GET", key) == token then
return redis.call("DEL", key)
end
return 0
`;
async function withLock<T>(
resource: string,
ttl: number,
fn: () => Promise<T>
): Promise<T> {
const token = crypto.randomUUID();
const acquired = await redis.eval(acquireLock, 1, `lock:${resource}`, token, ttl);
if (!acquired) throw new Error("Failed to acquire lock");
try {
return await fn();
} finally {
await redis.eval(releaseLock, 1, `lock:${resource}`, token);
}
}Anti-Patterns
- Storing large objects (>100KB) in Redis without compression
- Using `KEYS *` in production (blocks the server; use `SCAN` instead)
- Not setting TTL on cache entries (memory grows unbounded)
- Using pub/sub for durable messaging (messages are lost if no subscriber is connected)
- Relying on Redis as the sole data store without persistence strategy
- Not using pipelines for multiple sequential commands
Checklist
- [ ] Cache keys follow a consistent naming convention (`entity:id:field`)
- [ ] All cache entries have a TTL to prevent memory leaks
- [ ] `SCAN` used instead of `KEYS` for pattern matching in production
- [ ] Lua scripts used for operations requiring atomicity
- [ ] Streams used instead of pub/sub when durability is needed
- [ ] Connection pooling configured for high-throughput applications
- [ ] Rate limiting uses sliding window with sorted sets
- [ ] Distributed locks include fencing tokens and TTL
Read more
name: redis-patterns description: Redis patterns including caching strategies, pub/sub, streams for event processing, Lua scripts, and data structures
Redis Patterns
Caching Strategies
async function getUser(userId: string): Promise<User> {
const cacheKey = `user:${userId}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const user = await db.user.findUnique({ where: { id: userId } });
if (user) {
await redis.set(cacheKey, JSON.stringify(user), "EX", 3600);
}
return user;
}
async function invalidateUser(userId: string): Promise<void> {
await redis.del(`user:${userId}`);
await redis.del(`user:${userId}:orders`);
}
async function cacheAside<T>(
key: string,
ttlSeconds: number,
fetcher: () => Promise<T>
): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const value = await fetcher();
await redis.set(key, JSON.stringify(value), "EX", ttlSeconds);
return value;
}Rate Limiting with Sliding Window
async function isRateLimited(
clientId: string,
limit: number,
windowSeconds: number
): Promise<boolean> {
const key = `ratelimit:${clientId}`;
const now = Date.now();
const windowStart = now - windowSeconds * 1000;
const pipe = redis.multi();
pipe.zremrangebyscore(key, 0, windowStart);
pipe.zadd(key, now, `${now}:${crypto.randomUUID()}`);
pipe.zcard(key);
pipe.expire(key, windowSeconds);
const results = await pipe.exec();
const count = results[2][1] as number;
return count > limit;
}Pub/Sub
const subscriber = redis.duplicate();
await subscriber.subscribe("notifications", "orders");
subscriber.on("message", (channel, message) => {
const event = JSON.parse(message);
switch (channel) {
case "notifications":
handleNotification(event);
break;
case "orders":
handleOrderEvent(event);
break;
}
});
async function publishEvent(channel: string, event: object): Promise<void> {
await redis.publish(channel, JSON.stringify(event));
}Streams for Event Processing
async function produceEvent(stream: string, event: Record<string, string>) {
await redis.xadd(stream, "*", ...Object.entries(event).flat());
}
async function consumeEvents(
stream: string,
group: string,
consumer: string
) {
try {
await redis.xgroup("CREATE", stream, group, "0", "MKSTREAM");
} catch {
// group already exists
}
while (true) {
const results = await redis.xreadgroup(
"GROUP", group, consumer,
"COUNT", 10,
"BLOCK", 5000,
"STREAMS", stream, ">"
);
if (!results) continue;
for (const [, messages] of results) {
for (const [id, fields] of messages) {
await processMessage(fields);
await redis.xack(stream, group, id);
}
}
}
}Streams provide durable, consumer-group-based event processing with acknowledgment and replay.
Lua Script for Atomic Operations
const acquireLock = `
local key = KEYS[1]
local token = ARGV[1]
local ttl = ARGV[2]
if redis.call("SET", key, token, "NX", "EX", ttl) then
return 1
end
return 0
`;
const releaseLock = `
local key = KEYS[1]
local token = ARGV[1]
if redis.call("GET", key) == token then
return redis.call("DEL", key)
end
return 0
`;
async function withLock<T>(
resource: string,
ttl: number,
fn: () => Promise<T>
): Promise<T> {
const token = crypto.randomUUID();
const acquired = await redis.eval(acquireLock, 1, `lock:${resource}`, token, ttl);
if (!acquired) throw new Error("Failed to acquire lock");
try {
return await fn();
} finally {
await redis.eval(releaseLock, 1, `lock:${resource}`, token);
}
}Anti-Patterns
- Storing large objects (>100KB) in Redis without compression
- Using `KEYS *` in production (blocks the server; use `SCAN` instead)
- Not setting TTL on cache entries (memory grows unbounded)
- Using pub/sub for durable messaging (messages are lost if no subscriber is connected)
- Relying on Redis as the sole data store without persistence strategy
- Not using pipelines for multiple sequential commands
Checklist
- [ ] Cache keys follow a consistent naming convention (`entity:id:field`)
- [ ] All cache entries have a TTL to prevent memory leaks
- [ ] `SCAN` used instead of `KEYS` for pattern matching in production
- [ ] Lua scripts used for operations requiring atomicity
- [ ] Streams used instead of pub/sub when durability is needed
- [ ] Connection pooling configured for high-throughput applications
- [ ] Rate limiting uses sliding window with sorted sets
- [ ] Distributed locks include fencing tokens and TTL
The most comprehensive toolkit for Claude Code -- 135 agents, 35 curated skills (+400,000 via SkillKit), 42 commands, 176+ plugins, 20 hooks, 15 rules, 7 templates, 15 MCP configs, 26 companion apps, 53 ecosystem entries, and more.
Repo: rohitg00/awesome-claude-code-toolkit
Other skills on rohitg00-claude-code-toolkit.
- /accessibility-wcag
Web accessibility patterns for WCAG 2.2 compliance including ARIA, keyboard navigation, screen readers, and testing
Open skill - /agentkit-seo
Route broad or ambiguous AgentKit SEO work to the right module while keeping context scoped. Use when a request spans multiple surfaces, asks for overall digital-presence strategy, involves provider or install architecture, needs agent-context planning, or the correct platform
Open skill - /api-design-patterns
REST API design with resource naming, pagination, versioning, and OpenAPI spec generation
Open skill - /authentication-patterns
Authentication and authorization patterns including OAuth2, JWT, RBAC, session management, and PKCE flows
Open skill - /aws-cloud-patterns
AWS cloud patterns for Lambda, ECS, S3, DynamoDB, and Infrastructure as Code with CDK/Terraform
Open skill - /ci-cd-pipelines
CI/CD pipeline patterns for GitHub Actions, GitLab CI, testing strategies, and deployment automation
Open skill

