Redis and in-memory cache adapter with safe error handling for the Roastery CMS ecosystem.
@roastery-adapters/cache provides two core primitives for integrating cache into Roastery-based applications:
- Cache factory — A factory function (
cache) that initializes a Redis or in-memory mock cache service, integrated with Roastery's dependency injection system via@roastery/barista. - Safe decorator — A method decorator (
SafeCache) that wraps async methods with structured error handling for Redis connection failures, converting them into typedCacheUnavailableExceptionerrors.
| Tool | Purpose |
|---|---|
| Bun | Native RedisClient, runtime, test runner, and package manager |
| ioredis-mock | In-memory Redis mock for development and testing |
| tsup | Bundling to ESM + CJS with .d.ts generation |
| Knip | Unused exports and dependency detection |
| Husky + commitlint | Git hooks and conventional commit enforcement |
bun add @roastery-adapters/cachePeer dependencies (install alongside):
bun add @roastery/barista @roastery/terroir @roastery/beanscache initializes a cache service backed by real Redis or an in-memory mock, depending on the provided configuration. It is decorated with @roastery/barista for dependency injection.
import { cache } from '@roastery-adapters/cache';
const cache = cache({
CACHE_PROVIDER: 'REDIS',
REDIS_URL: 'redis://localhost:6379',
});| Option | Type | Required | Description |
|---|---|---|---|
CACHE_PROVIDER |
"REDIS" | "MEMORY" |
Yes | Selects the cache backend |
REDIS_URL |
string (URL) |
No | Redis connection URL (required when CACHE_PROVIDER is "REDIS") |
When CACHE_PROVIDER is "MEMORY" or REDIS_URL is not provided, the factory falls back to an in-memory mock (ioredis-mock).
The returned instance is typed as cacheInstance (alias for Bun's RedisClient).
SafeCache is a method decorator that catches Redis connection errors and re-throws them as CacheUnavailableException from @roastery/terroir.
import { SafeCache } from '@roastery-adapters/cache/decorators';
class UserCacheRepository {
@SafeCache('UserCacheRepository')
async get(key: string) {
return this.cache.get(key);
}
}The optional layerName parameter sets the context name included in the exception. If omitted, it defaults to the class name (target.constructor.name).
Handled error codes:
| Code | Cause |
|---|---|
ERR_REDIS_CONNECTION_CLOSED |
Redis connection was closed |
ERR_REDIS_AUTHENTICATION_FAILED |
Authentication to Redis failed |
ERR_REDIS_INVALID_RESPONSE |
Redis returned an unexpected response |
All matched errors are re-thrown as CacheUnavailableException. Unrecognized errors are also wrapped.
import { cache } from '@roastery-adapters/cache'; // cache factory function
import type { cacheInstance } from '@roastery-adapters/cache'; // RedisClient type alias
import { SafeCache } from '@roastery-adapters/cache/decorators'; // safe method decorator
import { CacheEnvDependenciesDTO } from '@roastery-adapters/cache/dtos'; // config schema + type
import { CacheProviderDTO } from '@roastery-adapters/cache/dtos'; // "REDIS" | "MEMORY" schema + type# Run tests
bun run test:unit
# Run tests with coverage
bun run test:coverage
# Build for distribution
bun run build
# Check for unused exports and dependencies
bun run knip
# Full setup (build + bun link)
bun run setupMIT