Skip to content

@geekmidas/cache ​

Unified caching interface with multiple backend implementations.

Declare the cache

A CacheClient is what a declared Cache hands back:

typescript
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:

typescript
export const cache = database.cache();  // storage is `database`, not a stage decision

Installation ​

bash
pnpm add @geekmidas/cache

Features ​

  • 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 ​

typescript
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 ​

typescript
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:

typescript
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) ​

typescript
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:

SpanAttributes
cache.getdb.system (redis or postgresql), cache.operation, cache.hit
cache.setdb.system, cache.operation
cache.deletedb.system, cache.operation

Never the key or the value: a key often names a user.

Usage with Rate Limiting ​

typescript
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:

typescript
interface Cache<T> {
  get(key: string): Promise<T | undefined>;
  set(key: string, value: T, options?: { ttl?: number }): Promise<void>;
  delete(key: string): Promise<void>;
}