State Backends
Quick start: See Tutorial 4 (Live State) for the simplest way to use state, and Tutorial 9 (Production) for Redis configuration.
Interface
All state backends implement:
typescript
interface StateBackend {
get<T>(key: string): Promise<T | undefined>;
set<T>(key: string, value: T): Promise<void>;
delete(key: string): Promise<boolean>;
subscribe(key: string, handler: (key: string, value: unknown) => void): () => void;
publish(key: string, value: unknown): Promise<void>;
}Built-in Backends
MemoryBackend (default)
In-memory Map + EventEmitter. Zero dependencies. Suitable for single-process deployments and development.
typescript
import { DatasoleServer, MemoryBackend } from 'datasole/server';
const ds = new DatasoleServer<AppContract>({
stateBackend: new MemoryBackend(), // This is the default
});RedisBackend
Requires ioredis peer dependency. Uses Redis pub/sub for cross-process state notifications. Required for pm2 cluster deployments.
typescript
import { DatasoleServer, RedisBackend } from 'datasole/server';
const backend = new RedisBackend({
url: 'redis://localhost:6379',
prefix: 'ds:',
});
await backend.connect();
const ds = new DatasoleServer<AppContract>({ stateBackend: backend });PostgresBackend
Requires pg peer dependency. Persists state to Postgres with LISTEN/NOTIFY for pub/sub. Use for durable persistence.
typescript
import { DatasoleServer, PostgresBackend } from 'datasole/server';
const backend = new PostgresBackend({
connectionString: 'postgres://user:pass@localhost:5432/mydb',
tableName: 'datasole_state',
});
await backend.connect();
const ds = new DatasoleServer<AppContract>({ stateBackend: backend });Session Persistence
The SessionManager sits on top of any state backend to provide per-user state that survives disconnections:
typescript
const redisBackend = new RedisBackend({ url: 'redis://localhost:6379' });
await redisBackend.connect();
const ds = new DatasoleServer<AppContract>({
stateBackend: redisBackend,
session: {
flushThreshold: 10, // Persist after 10 mutations
flushIntervalMs: 5000, // Or every 5 seconds
},
});
// Per-user read/write
ds.primitives.sessions.set('user-123', 'theme', 'dark');
const theme = ds.primitives.sessions.get<string>('user-123', 'theme');
// Change streams
ds.primitives.sessions.onChange((userId, key, value, version) => {
console.log(`${userId} changed ${key}`);
});Custom Backends
Implement the StateBackend interface and pass to DatasoleServer:
typescript
class MyCustomBackend implements StateBackend {
async get<T>(key: string): Promise<T | undefined> {
/* ... */
}
async set<T>(key: string, value: T): Promise<void> {
/* ... */
}
async delete(key: string): Promise<boolean> {
/* ... */
}
subscribe(key: string, handler: (key: string, value: unknown) => void): () => void {
/* ... */
}
async publish(key: string, value: unknown): Promise<void> {
/* ... */
}
}
const ds = new DatasoleServer<AppContract>({ stateBackend: new MyCustomBackend() });