encore-cache — Claude Code skill: Redis caching
Adds typed Redis caching patterns for Encore.ts. Use it for TTL-based values, counters, temporary tokens, and frequently read data.
npx skills add encoredev/skills --skill encore-cache -a claude-code
npx skills add encoredev/skills --skill cache -a claude-codeWhat encore-cache does
This skill guides Redis usage in Encore.ts through CacheCluster and typed keyspaces from encore.dev/storage/cache. It covers strings, numbers, structs, lists, and sets, with key patterns that can use several fields. Integer keyspaces provide atomic increment and decrement operations.
The material also covers expiry helpers such as expireIn, expireDailyAt, neverExpire, and keepTTL, plus setIfNotExists and replace. A missing get() returns undefined; key conflicts are represented by CacheKeyExists and CacheMiss.
Local development runs against an in-memory Redis capped at about 100 keys, so it is not suitable for load testing. Durable data belongs in encore-database with Postgres or encore-bucket for object storage.
What it helps with
- store short-lived tokens with an expiry
- count requests using atomic increments
- cache data behind a frequently read path
- define Redis keys from multiple typed fields
Example prompts for the agent
- Set up a typed Redis cache in Encore.ts for temporary tokens with an expiry.
- Create a per-user request counter and increment it atomically.
- Define a
StructKeyspacefor user profiles and handle cache misses.
How to install encore-cache
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-cache -a claude-codenpx skills add encoredev/skills --skill cache -a claude-code2Manually: put the files in place
~/.claude/skills/cache/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/cache/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill cache -a codex2Manually: put the files in place
.agents/skills/cache/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill cache -a cursor2Manually: put the files in place
.cursor/skills/cache/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install encoredev/skills cache2Manually: put the files in place
.github/skills/cache/SKILL.md1Skills CLI (Vercel)
npx skills add encoredev/skills --skill cache -a gemini-cli2Platform CLI
gemini skills install https://github.com/encoredev/skills --path encore/cacheManually: put the files in place
.devin/skills/cache/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add encoredev/skills --skill cache -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about encore-cache
How do I install encore-cache in Claude Code?
Run npx skills add encoredev/skills --skill encore-cache -a claude-code, then run npx skills add encoredev/skills --skill cache -a claude-code. Verify the skill at ~/.claude/skills/cache/SKILL.md.
What does encore-cache do?
It gives the agent patterns for typed Redis caching in Encore.ts, including keyspaces, expiry rules, atomic counters, and conditional writes.
Is local development suitable for load testing?
No. Local development uses in-memory Redis with a cap of about 100 keys. Use encore-database or encore-bucket for durable data.
Source text
SKILL.md · Apache-2.0
---
name: encore-cache
description: Cache data in Redis from Encore.ts using `CacheCluster` and typed keyspaces from `encore.dev/storage/cache`. Type-safe key/value access with TTLs, atomic increments, and per-keyspace data shapes.
when_to_use: >-
User wants to cache values, store ephemeral state, rate-limit by counter, build a leaderboard, speed up a hot read, or store short-lived tokens. Covers `CacheCluster`, `StringKeyspace`, `IntKeyspace`, `FloatKeyspace`, `StructKeyspace`, list and set keyspaces, TTL helpers (`expireIn`, `expireDailyAt`, `neverExpire`, `keepTTL`), atomic `increment`/`decrement`, `setIfNotExists`, `replace`, eviction policies, and `CacheMiss`/`CacheKeyExists` errors. Trigger phrases: "cache this", "Redis", "key-value store", "rate limit", "TTL", "expire after", "in-memory store", "session token store", "leaderboard counter".
---
# Encore Caching (Redis)
## Instructions
Encore's cache is a typed wrapper around Redis. Declare a `CacheCluster` once, then create `Keyspace` objects for each shape of data you need.
### Cluster
```typescript
import { CacheCluster } from "encore.dev/storage/cache";
const cluster = new CacheCluster("my-cache", {
evictionPolicy: "allkeys-lru",
});
```
Reference a cluster from another service: `const cluster = CacheCluster.named("my-cache");`
Eviction policies: `"allkeys-lru"` (default), `"noeviction"`, `"allkeys-lfu"`, `"allkeys-random"`, `"volatile-lru"`, `"volatile-lfu"`, `"volatile-ttl"`, `"volatile-random"`.
### Keyspace types
Each keyspace has a key shape (used to build the Redis key from `keyPattern`) and a value type.
```typescript
import {
StringKeyspace,
IntKeyspace,
FloatKeyspace,
StructKeyspace,
StringListKeyspace,
NumberListKeyspace,
StringSetKeyspace,
NumberSetKeyspace,
expireIn,
} from "encore.dev/storage/cache";
// Strings
const tokens = new StringKeyspace<{ tokenId: string }>(cluster, {
keyPattern: "token/:tokenId",
defaultExpiry: expireIn(3600 * 1000),
});
await tokens.set({ tokenId: "abc" }, "value");
const val = await tokens.get({ tokenId: "abc" }); // undefined on miss
// Integers (atomic counters)
const counters = new IntKeyspace<{ userId: string }>(cluster, {
keyPattern: "requests/:userId",
defaultExpiry: expireIn(10 * 1000),
});
const count = await counters.increment({ userId: "user123" }, 1);
// Structs (JSON)
interface UserProfile { name: string; email: string; }
const profiles = new StructKeyspace<{ userId: string }, UserProfile>(cluster, {
keyPattern: "profile/:userId",
defaultExpiry: expireIn(3600 * 1000),
});
await profiles.set({ userId: "123" }, { name: "Alice", email: "[email protected]" });
// Lists
const recent = new StringListKeyspace<{ userId: string }>(cluster, {
keyPattern: "recent/:userId",
});
await recent.pushRight({ userId: "user123" }, "item1", "item2");
// Sets
const tags = new StringSetKeyspace<{ articleId: string }>(cluster, {
keyPattern: "tags/:articleId",
});
await tags.add({ articleId: "post1" }, "typescript", "encore");
const has = await tags.contains({ articleId: "post1" }, "typescript");
```
### Multi-field key patterns
```typescript
interface Key { userId: string; resourcePath: string; }
const requests = new IntKeyspace<Key>(cluster, {
keyPattern: "requests/:userId/:resourcePath",
defaultExpiry: expireIn(10 * 1000),
});
```
### Expiry helpers
```typescript
import {
expireIn, // milliseconds
expireInSeconds,
expireInMinutes,
expireInHours,
expireDailyAt, // a specific UTC time each day
neverExpire,
keepTTL, // keep existing TTL when updating
} from "encore.dev/storage/cache";
```
### Write options
```typescript
await keyspace.set(key, value, { expiry: expireInMinutes(30) });
await keyspace.set(key, value, { expiry: keepTTL });
await keyspace.setIfNotExists(key, value); // throws CacheKeyExists if present
await keyspace.replace(key, value); // throws CacheMiss if absent
```
### Errors
```typescript
import { CacheMiss, CacheKeyExists } from "encore.dev/storage/cache";
const value = await keyspace.get(key); // undefined on miss (does not throw)
```
## Guidelines
- Declare `CacheCluster` and keyspaces at package level.
- Pick the most specific keyspace type — `IntKeyspace` for counters gives you atomic `increment`/`decrement` for free.
- `get()` returns `undefined` on miss; `replace()` and `setIfNotExists()` throw on conflict.
- Local development uses an in-memory Redis with a ~100-key cap — don't load-test it.
- For durable storage, use `encore-database` (Postgres) or `encore-bucket` (object storage) instead.
© encoredev · Apache-2.0 · encoredev/skills