Semola

Cache

Typed Redis cache with TTL and optional key prefix

Store and load typed values in Redis. Values are JSON by default.

Needs a Bun.RedisClient.

Import

import { Cache } from "semola/cache";

Quick start

This stores a typed user for one minute, reads it from the prefixed Redis key, then deletes it.

const users = new Cache<{ name: string; email: string }>({
  redis: redisClient,
  ttl: 60_000, // milliseconds (Redis PX)
  prefix: "user",
});

await users.set("1", { name: "Ada", email: "ada@example.com" });

const user = await users.get("1");
// key in Redis: "user:1"

await users.delete("1");

get throws if the key is missing. ttl can be a number or (key, value) => number. Omit it (or pass undefined) for no expiry.

Soft-disable

This cache performs no writes or deletes, and every read behaves like a miss.

const cache = new Cache({
  redis: redisClient,
  enabled: false,
});

When enabled is false, set / delete are no-ops and get behaves as a miss. Handy for local feature flags without branching call sites.

Custom serialization

Pass serializer / deserializer if JSON is not enough. Keep them inverse of each other.

Examples

Store a value with set()

set() serializes the value, applies the configured TTL, and returns the original typed value.

await users.set("1", { name: "Ada", email: "ada@example.com" });

Read a value with get()

get() resolves the prefixed key, deserializes its value, and throws on a miss.

const user = await users.get("1");

Remove a value with delete()

delete() removes the resolved Redis key and returns Redis's deletion count.

const deleted = await users.delete("1");

Dynamic TTL

The TTL callback keeps admin sessions for one hour and all other sessions for one minute.

const sessions = new Cache<{ userId: string }>({
  redis: redisClient,
  prefix: "session",
  ttl: (_key, value) => (value.userId.startsWith("admin:") ? 3_600_000 : 60_000),
});

Soft-disable in development

The same call sites keep working in development, but cache operations only reach Redis in production.

const cache = new Cache({
  redis: redisClient,
  enabled: process.env.NODE_ENV === "production",
  prefix: "user",
});

Custom serializer

Values are encoded as base64 before storage and restored to Uint8Array after reading.

const blobs = new Cache<Uint8Array>({
  redis: redisClient,
  serializer: (value) => Buffer.from(value).toString("base64"),
  deserializer: (raw) => new Uint8Array(Buffer.from(raw, "base64")),
});

Reference

OptionDefaultMeaning
redisrequiredBun.RedisClient
ttlnonems, or (key, value) => number; invalid values throw
enabledtrueSoft on/off switch
prefix-Key prefix (prefix:key)
serializerJSON.stringifyValue → string
deserializerJSON.parseString → value

Methods

MethodMeaning
get(key)Load value; throws on miss
set(key, value)Store value
delete(key)Remove key

On this page