Redis-backed CachePort provider for Beignet applications.
The provider installs ctx.ports.cache using
ioredis and exposes the Redis client only
as an escape hatch for Redis-specific features.
bun add @beignet/provider-redis ioredis
import { createNextServer } from "@beignet/next";
import { definePorts } from "@beignet/core/ports";
import { redisProvider } from "@beignet/provider-redis";
import { routes } from "@/server/routes";
// Set environment variables:
// REDIS_URL=redis://localhost:6379
// REDIS_DB=0 (optional)
const appPorts = definePorts({});
export const server = await createNextServer({
ports: appPorts,
providers: [redisProvider],
createContext: ({ ports }) => ({
ports,
}),
routes,
});
Once the provider is registered, your ports will include a cache property:
// In your use case
async function getUserProfile(ctx: AppCtx) {
const userId = ctx.actor.type === "user" ? ctx.actor.id : undefined;
if (!userId) throw new Error("User actor required.");
const cacheKey = `user:${userId}:profile`;
// Try to get from cache
const cached = await ctx.ports.cache.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// Fetch from database
const profile = await ctx.ports.db.users.findById(userId);
// Store in cache for 1 hour
await ctx.ports.cache.set(
cacheKey,
JSON.stringify(profile),
{ ttlSeconds: 3600 }
);
return profile;
}
The Redis provider reads configuration from environment variables with the REDIS_ prefix:
| Variable | Required | Description | Example |
|---|---|---|---|
REDIS_URL |
Yes | Redis connection URL | redis://localhost:6379 |
REDIS_DB |
No | Redis database number (default: 0) | 0 |
The provider extends your ports with the following cache interface:
get(key: string): Promise<string | null>Get a value from the cache.
const value = await ctx.ports.cache.get("my-key");
set(key: string, value: string, options?: { ttlSeconds?: number }): Promise<void>Set a value in the cache with optional TTL (time-to-live) in seconds.
// Without TTL (persists forever)
await ctx.ports.cache.set("key", "value");
// With TTL (expires after 1 hour)
await ctx.ports.cache.set("key", "value", { ttlSeconds: 3600 });
delete(key: string): Promise<boolean>Delete a key from the cache. Returns true when a key was deleted.
const deleted = await ctx.ports.cache.delete("my-key");
has(key: string): Promise<boolean>Check if a key exists in the cache.
const exists = await ctx.ports.cache.has("my-key");
remember(key: string, factory: () => Promise<string>, options?: { ttlSeconds?: number }): Promise<string>Return the cached value when present. On a miss, compute, store, and return the factory value.
const value = await ctx.ports.cache.remember(
"my-key",
async () => JSON.stringify(await loadExpensiveData()),
{ ttlSeconds: 300 },
);
client: RedisAccess the underlying ioredis client for advanced operations.
// Use ioredis methods directly
await ctx.ports.cache.client.expire("key", 300);
await ctx.ports.cache.client.incr("counter");
When @beignet/devtools is installed before this provider, Redis cache
operations appear under the dashboard's Cache watcher.
The provider records cache.get, cache.set, cache.delete, cache.has, and
cache.remember events with the cache key, hit/miss or deleted status, TTL, and
duration. Cached values are not recorded.
To get proper type inference for the cache port, extend your ports type:
import type { RedisCachePort } from "@beignet/provider-redis";
// Your base ports, if any
const basePorts = definePorts({});
// After using redisProvider, your ports will have this shape:
type AppPorts = typeof basePorts & {
cache: RedisCachePort;
};
The Redis provider:
setup: Connects to Redis and returns the cache portstop: Gracefully closes the Redis connectionThe provider will throw errors in these cases:
REDIS_URL environment variableMake sure to handle these during application startup.
MIT