Nfs add cache
Skill juncoding/nextjs-fullstack-starter/skills/nfs-add-cache
Claude Code plugin: scaffold and maintain lightweight back-office apps on pure Next.js (App Router) — Server Components for reads, Server Actions for writes, services in src/server/modules/. No tRPC.
npx -y skills add juncoding/nextjs-fullstack-starter --skill nfs-add-cacheAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Retrofit Redis-backed cache handling into an existing project scaffolded with nextjs-fullstack-starter. Use when the user wants to add Redis, share Next.js cache across processes / deploys, speed up frequent reads, or any 'add caching to this project' request. Adds ioredis, a docker-compose redis service, wires Next.js's experimental cacheHandler to use Redis, adds a typed cache helper at src/server/lib/cache.ts, and REDIS_URL env. Refuses to run on projects that don't have the scaffolded structure.
SKILL.md
5.4 KB, as published. Nobody here has run it
Retrofit Redis-backed cache
Use when a project from nextjs-fullstack-starter (which uses Next.js's in-process cache by default) outgrows that. Typical triggers:
- The user is deploying multiple Next.js processes / containers and wants a shared cache.
- A blue-green deploy throws away the warm cache every release and that's becoming painful.
- The user wants to use
src/server/lib/cache.tsas a general-purpose Redis helper for non-Next.js caches too (rate limits, queues, etc.).
If the project ISN'T at one of those tipping points, push back gently — Next.js's default cache is fine until measurably not.
Pre-flight checks
Refuse if any of these is true:
src/server/modules/doesn't exist — the project wasn't scaffolded with this plugin.src/server/lib/cache.tsalready exists — Redis is already wired. Suggest/nfs-review-projectfor an audit instead.
Plan
- Add
ioredisto dependencies. Add@neshca/cache-handlerif the user wants the Next.js cache itself to go through Redis. - Add
redisservice todocker-compose.yml(under thecacheprofile so it doesn't always run). - Add
REDIS_URLtosrc/env.tsand.env.example. - Write
src/server/lib/cache.ts— a typed wrapper exposingget,set,del, and acached(fn, key, ttl)helper. - (Optional) Configure
next.config.ts'scacheHandlerto use@neshca/cache-handleragainst Redis. - Update
CLAUDE.mdwith the new pattern.
The cache helper
// src/server/lib/cache.ts
import "server-only";
import Redis from "ioredis";
import { env } from "@/env";
import { logger } from "@/server/lib/logger";
const log = logger.child({ module: "cache" });
let client: Redis | null = null;
function getClient(): Redis {
if (!client && env.REDIS_URL) {
client = new Redis(env.REDIS_URL, {
lazyConnect: true,
maxRetriesPerRequest: 2,
retryStrategy: (times) => Math.min(times * 100, 2000),
});
client.on("error", (err) => log.warn({ err }, "redis error"));
}
if (!client) throw new Error("REDIS_URL not set");
return client;
}
export const cache = {
async get<T>(key: string): Promise<T | null> {
try {
const raw = await getClient().get(key);
return raw ? (JSON.parse(raw) as T) : null;
} catch (err) {
log.warn({ err, key }, "cache.get failed");
return null;
}
},
async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {
try {
const raw = JSON.stringify(value);
if (ttlSeconds) await getClient().setex(key, ttlSeconds, raw);
else await getClient().set(key, raw);
} catch (err) {
log.warn({ err, key }, "cache.set failed");
}
},
async del(key: string): Promise<void> {
try {
await getClient().del(key);
} catch (err) {
log.warn({ err, key }, "cache.del failed");
}
},
/**
* Read-through cache wrapper. Returns the cached value if present, else
* computes via `compute`, stores, and returns.
*/
async cached<T>(key: string, ttlSeconds: number, compute: () => Promise<T>): Promise<T> {
const hit = await this.get<T>(key);
if (hit !== null) return hit;
const fresh = await compute();
await this.set(key, fresh, ttlSeconds);
return fresh;
},
};
When to reach for cache.cached vs. "use cache" directive
| Need | Use |
|---|---|
| Cache a Server Component / service read for Next.js to invalidate by tag | "use cache" + cacheTag + updateTag (built-in) |
| Cache something that isn't a server read (rate-limit counter, queue, ad-hoc key) | cache.get / cache.set directly |
| Cache an expensive computation across processes | cache.cached(key, ttl, fn) |
The two systems coexist — Next.js's "use cache" handles the server-render cache; cache.* is your own general-purpose Redis client.
Update CLAUDE.md
Add to the stack list:
- **Redis** via `ioredis`. Helper at `src/server/lib/cache.ts`. Use for non-Next.js caches (rate limits, queues, ad-hoc keys). Next.js's `"use cache"` + `updateTag` continue to handle server-render caching independently.
Update the architecture section to mention which keys to cache and which not (per the caching.md reference).
Verification
pnpm install
docker compose --profile cache up -d # starts the redis container
REDIS_URL=redis://localhost:6379 pnpm verify
Anti-patterns to refuse
- Replacing the Next.js cache wholesale with the ioredis helper. They serve different purposes. Server-render caching belongs to Next.js's primitives; ad-hoc caching is what the helper is for.
- Caching per-user data without explicit user-scoped keys. Easy way to leak data across users. If you cache anything user-scoped, the key MUST include the userId.
- Using
cache.*to "fix" slow queries. Caching is a workaround. If a query is slow, fix the query or add an index first; only cache when there's no further optimization room.