Overview
One key value interface, three backends: in-memory, Redis, or Deno KV, with atomic operations built in.
createStore(config) returns a key value store. mode picks the backend, and every backend implements the exact same interface, so switching from memory to Redis later is a config change, not a rewrite.
import { createStore } from '@codexa/core/store';
const sessions = await createStore({ mode: 'memory' });
await sessions.set('session:u1', { userId: 'u1' }, { ttl: 900 });
const session = await sessions.get<{ userId: string }>('session:u1');The three modes
await createStore({ mode: 'memory' });
await createStore({ mode: 'redis', redisClient: redis.getClient() });
await createStore({ mode: 'kv', kvPath: './data/store.db' });mode: 'redis' and mode: 'kv' both fall back to an in-memory store if the backend fails to connect, memory being unreachable is impossible, so this fallback is silent by default. A Redis outage does not fail your store setup, it quietly starts handing out an ephemeral in-memory store instead, and only a warning is logged. Pass fallbackToMemory: false if a production store failing to reach its real backend should throw instead of degrading silently.
await createStore({
mode: 'redis',
redisClient: redis.getClient(),
fallbackToMemory: false, // throw instead of silently degrading to memory
});Deno KV additionally requires the unstable flag at runtime.
deno run --unstable-kv --allow-read --allow-write main.tsThe store interface
Every mode implements the same core operations.
Prop
Type
Atomic helpers
Two operations exist specifically for correctness under concurrency, reading a value and later acting on it is never safe to split into two separate calls if another request could run in between.
// Atomic read-and-delete, useful for a one-time token
const transaction = await sessions.take<AuthTransaction>('txn:123');
if (transaction === null) {
throw new Error('Transaction is missing, expired, or already consumed.');
}// Atomic compare-and-set, retried until it succeeds
while (true) {
const current = await sessions.get<AuthTransaction>('txn:123');
if (current === null) throw new Error('Transaction is missing.');
const next = { ...current, loginVerified: true };
const updated = await sessions.compareAndSet('txn:123', current, next);
if (updated) break; // someone else updated it first, retry
}Convenience methods on top
A few methods build on the core interface, available on the instance createStore returns.
await sessions.getOrSet('u1:profile', () => fetchProfile('u1'), { ttl: 300 });
await sessions.delPattern('session:*');
await sessions.mset({ 'a': 1, 'b': 2 }, { ttl: 60 });
const values = await sessions.mget<number>(['a', 'b']); // Map<string, number | null>
await sessions.stats(); // { type, keyCount, uptimeMs, backend }
await sessions.close(); // alias for quit()