Implement query result caching with Redis and proper invalidation strategies for Prisma 6. Use when optimizing frequently accessed data, improving read-heavy application performance, or reducing database load through caching.
Implements Redis query caching for Prisma 6 with cache-aside pattern, consistent key generation, and mutation-triggered invalidation. Use when optimizing read-heavy applications, reducing database load, or improving response times for frequently accessed data.
/plugin marketplace add djankies/claude-configs/plugin install prisma-6@claude-configsThis skill is limited to using the following tools:
references/advanced-examples.mdreferences/common-pitfalls.mdreferences/invalidation-patterns.mdreferences/redis-configuration.mdEfficient query result caching for Prisma 6 applications using Redis: cache key generation, invalidation strategies, TTL management, and when caching provides value.
Phase 2: Implement Cache Layer Set up Redis with connection pooling; create cache wrapper around Prisma queries; implement consistent cache key generation; add cache read with database fallback.
Phase 3: Implement Invalidation Identify mutations affecting cached data; add invalidation to update/delete operations; handle bulk operations and cascading invalidation; test across scenarios.
Phase 4: Configure TTL Determine appropriate TTL per data type; implement time-based expiration; add event-based invalidation for critical data; monitor hit rates and adjust. </workflow>
<decision-tree> ## When to CacheStrong Candidates:
: homepage data, navigation, popular results, trending content
Weak Candidates:
Decision Tree:
Read/write ratio > 10:1?
├─ Yes: Strong candidate
│ └─ Data stale 1+ minutes acceptable?
│ ├─ Yes: Long TTL (5-60min) + event invalidation
│ └─ No: Short TTL (10-60sec) + aggressive invalidation
└─ No: Ratio > 3:1?
├─ Yes: Moderate candidate, if query > 100ms → short TTL (30-120sec)
└─ No: Skip; optimize query/indexes/pooling instead
</decision-tree>
<examples>
## Basic Cache Implementation
Example 1: Cache-Aside Pattern
import { PrismaClient } from '@prisma/client';
import { Redis } from 'ioredis';
const prisma = new PrismaClient();
const redis = new Redis({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT || '6379'),
maxRetriesPerRequest: 3,
});
async function getCachedUser(userId: string) {
const cacheKey = `user:${userId}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, email: true, name: true, role: true },
});
if (user) await redis.setex(cacheKey, 300, JSON.stringify(user));
return user;
}
Example 2: Consistent Key Generation
import crypto from 'crypto';
function generateCacheKey(entity: string, query: Record<string, unknown>): string {
const sortedQuery = Object.keys(query)
.sort()
.reduce((acc, key) => {
acc[key] = query[key];
return acc;
}, {} as Record<string, unknown>);
const queryHash = crypto
.createHash('sha256')
.update(JSON.stringify(sortedQuery))
.digest('hex')
.slice(0, 16);
return `${entity}:${queryHash}`;
}
async function getCachedPosts(filters: {
authorId?: string;
published?: boolean;
tags?: string[];
}) {
const cacheKey = generateCacheKey('posts', filters);
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const posts = await prisma.post.findMany({
where: filters,
select: { id: true, title: true, createdAt: true },
});
await redis.setex(cacheKey, 120, JSON.stringify(posts));
return posts;
}
Example 3: Cache Invalidation on Mutation
async function updatePost(postId: string, data: { title?: string; content?: string }) {
const post = await prisma.post.update({ where: { id: postId }, data });
await Promise.all([
redis.del(`post:${postId}`),
redis.del(`posts:author:${post.authorId}`),
redis.keys('posts:*').then((keys) => keys.length > 0 && redis.del(...keys)),
]);
return post;
}
Note: redis.keys() with patterns is slow on large keysets; use SCAN or maintain key sets.
Example 4: TTL Strategy
const TTL = {
user_profile: 600,
user_settings: 300,
posts_list: 120,
post_detail: 180,
popular_posts: 60,
real_time_stats: 10,
};
async function cacheWithTTL<T>(
key: string,
ttlType: keyof typeof TTL,
fetchFn: () => Promise<T>
): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await fetchFn();
await redis.setex(key, TTL[ttlType], JSON.stringify(data));
return data;
}
</examples>
<constraints>
**MUST:**
* Use cache-aside pattern (not cache-through)
* Consistent cache key generation (no random/timestamp components)
* Invalidate cache on all mutations affecting cached data
* Graceful Redis failure handling with database fallback
* JSON serialization (consistent with Prisma types)
* TTL on all cached values (never infinite)
* Thorough cache invalidation testing
SHOULD:
Redis SCAN vs KEYS for pattern matching
NEVER:
Invalidation Testing: Verify all mutations invalidate correct keys; test cascading invalidation for related entities; confirm bulk operations invalidate list caches; ensure no stale data post-mutation.
Performance: Measure query latency with/without cache; target >50% latency reduction; monitor P95/P99 improvements; verify caching doesn't increase memory pressure.
Redis Health: Monitor connection pool utilization, memory usage (set maxmemory-policy), connection failures; test application behavior when Redis is unavailable. </validation>
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.