@geekmidas/cache
Unified caching interface with multiple backend implementations.
Declare the cache
A CacheClient is what a declared Cache hands back:
import { Cache } from '@geekmidas/constructs/cache';
export const sessions = new Cache('Sessions');Where it lives follows the deploy target — Upstash on AWS, a table in the database on a server — because the same application code caches into either. The scheme in the injected URL picks the driver, so an app caching in Postgres never resolves a Redis client.
To say it caches in a particular database rather than leaving the choice to the deployment, declare it from that database — entries become a table inside it, and the config no longer moves it:
export const cache = database.cache(); // storage is `database`, not a stage decisionInstallation
pnpm add @geekmidas/cacheFeatures
- Type-safe cache with TypeScript generics
- Consistent async API across all backends
- TTL (time-to-live) support
- Multiple implementations: InMemoryCache, UpstashCache, FileCache, ExpoSecureCache
- Easy testing with swappable backends
Package Exports
/- Core cache interface/memory- In-memory cache implementation/upstash- Upstash Redis cache/file- File-based cache (JSON on disk)/expo- Expo Secure Store cache (React Native)
Basic Usage
In-Memory Cache
import { InMemoryCache } from '@geekmidas/cache/memory';
interface User {
id: string;
name: string;
email: string;
}
const cache = new InMemoryCache<User>();
// Set with TTL (in seconds)
await cache.set('user:123', { id: '123', name: 'John', email: 'john@example.com' }, { ttl: 3600 });
// Get value
const user = await cache.get('user:123');
// Delete value
await cache.delete('user:123');Upstash Redis Cache
import { UpstashCache } from '@geekmidas/cache/upstash';
const cache = new UpstashCache<User>({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
// Same API as InMemoryCache
await cache.set('user:123', userData, { ttl: 3600 });
const user = await cache.get('user:123');File Cache
Persistent file-based cache for CLI tools and build systems:
import { FileCache } from '@geekmidas/cache/file';
// Default path: process.cwd()/.gkm/cache.json
const cache = new FileCache();
// Custom path and TTL
const cache = new FileCache({
path: '/tmp/my-app/cache.json',
ttl: 3600, // 1 hour default TTL
});
// Same API as other implementations
await cache.set('build:hash', 'abc123', 300);
const hash = await cache.get('build:hash');
const remaining = await cache.ttl('build:hash');File locking ensures safe concurrent and cross-process writes. Parent directories are created automatically.
Expo Secure Store Cache (React Native)
import { ExpoSecureCache } from '@geekmidas/cache/expo';
const cache = new ExpoSecureCache<string>();
// Store sensitive data securely
await cache.set('auth_token', 'secret-token');
const token = await cache.get('auth_token');Tracing
The Redis (redis://, rediss://) and Postgres (postgres://) drivers record a CLIENT span per call through the global OpenTelemetry tracer — a no-op unless a provider is registered:
| Span | Attributes |
|---|---|
cache.get | db.system (redis or postgresql), cache.operation, cache.hit |
cache.set | db.system, cache.operation |
cache.delete | db.system, cache.operation |
Never the key or the value: a key often names a user.
Usage with Rate Limiting
import { InMemoryCache } from '@geekmidas/cache/memory';
import { rateLimit } from '@geekmidas/rate-limit';
const limiter = rateLimit({
limit: 100,
windowMs: 60000,
cache: new InMemoryCache(),
});Cache Interface
All cache implementations follow this interface:
interface Cache<T> {
get(key: string): Promise<T | undefined>;
set(key: string, value: T, options?: { ttl?: number }): Promise<void>;
delete(key: string): Promise<void>;
}