Skip to main content

Custom Context Store

Implement a custom context store for persistence, distributed systems, or specialized storage backends.

Interface

Context stores implement ICNSStimulationContextStore:

interface ICNSStimulationContextStore {
get<T>(): T | undefined;
set<T>(value: T): void;
}

The default implementation is an in-memory store. For long-lived sagas or distributed systems, you can provide a persistent or shared store.

Redis-backed context store

import { ICNSStimulationContextStore } from '@cnstra/core';
import { RedisClient } from 'redis';

class RedisContextStore implements ICNSStimulationContextStore {
constructor(
private client: RedisClient,
private sessionId: string
) {}

get<T>(): T | undefined {
const raw = this.client.getSync(`cnstra:ctx:${this.sessionId}`);
return raw ? JSON.parse(raw) : undefined;
}

set<T>(value: T): void {
this.client.setSync(
`cnstra:ctx:${this.sessionId}`,
JSON.stringify(value),
'EX',
3600 // 1 hour TTL
);
}
}

// Use in stimulation
await cns.stimulate(signal, {
createContextStore: () => new RedisContextStore(redisClient, 'session-123')
});

Database-backed context store

class DBContextStore implements ICNSStimulationContextStore {
constructor(
private db: DatabaseClient,
private runId: string
) {}

get<T>(): T | undefined {
const row = this.db.query(
'SELECT data FROM context_store WHERE run_id = ?',
[this.runId]
);
return row ? JSON.parse(row.data) : undefined;
}

set<T>(value: T): void {
this.db.execute(
'INSERT INTO context_store (run_id, data) VALUES (?, ?) ON CONFLICT(run_id) DO UPDATE SET data = ?',
[this.runId, JSON.stringify(value), JSON.stringify(value)]
);
}
}

await cns.stimulate(signal, {
createContextStore: () => new DBContextStore(db, 'run-456')
});

OIMDB-backed context store

For reactive frontend state:

import { db } from './oimdb-instance';

class OIMDBContextStore implements ICNSStimulationContextStore {
constructor(private runId: string) {}

get<T>(): T | undefined {
const record = db.context.selectByPrimaryKey({ runId: this.runId });
return record?.data as T | undefined;
}

set<T>(value: T): void {
db.context.upsertOne({ runId: this.runId, data: value });
}
}

await cns.stimulate(signal, {
createContextStore: () => new OIMDBContextStore('run-789')
});

Reusing context for recovery

When an error occurs, save the context store and retry:

let savedContext: ICNSStimulationContextStore | undefined;

await cns.stimulate(signal, {
onResponse: (r) => {
if (r.error) {
savedContext = r.contextStore; // capture for retry
}
}
});

// Retry with same context
if (savedContext) {
await cns.stimulate(retrySignal, { ctx: savedContext });
}

Tips

  • Serialization: Store only JSON-serializable data (no functions, Dates, etc.) unless using a custom serializer.
  • TTL: For distributed stores (Redis, DB), set TTL or cleanup policies to avoid unbounded growth.
  • Performance: Persistent stores add I/O overhead; use in-memory stores for high-throughput short-lived flows.
  • Consistency: In distributed systems, ensure your store supports read-your-own-writes semantics if neurons might run on different nodes.