mcpField
E
encore-cache

encore-cache

github.com/encoredevData & databases30430 installsUpdated 2d ago
Repository

About encore-cache

A guide to Encore’s typed Redis cache wrapper for developers caching data in Encore services. It uses clusters and keyspaces to define value types, key patterns, and expiry behavior.

Key features

  1. 01Declare cache clusters and keyspaces
  2. 02Use multiple keyspace value types
  3. 03Configure key patterns and expiry
  4. 04Use conditional writes and atomic counters
  5. 05Handle cache misses and key conflicts

Use cases

  1. 01Cache data by user or resource path
  2. 02Increment or decrement counters
  3. 03Set expiry times for cached values

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/encoredev/skills --skill encore-cache

After installing, type/encore-cachein Claude Code to confirm it loaded.

Details

From the project README

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
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.

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
interface Key { userId: string; resourcePath: string; }

const requests = new IntKeyspace<Key>(cluster, {
  keyPattern: "requests/:userId/:resourcePath",
  defaultExpiry: expireIn(10 * 1000),
});
Expiry helpers
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
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
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.

Comments

Markdown supported