RecordStore
JSON record storage methods, capabilities, TTL, filters, and bundled adapters.
RecordStore is the JSON record capability from @use-crux/core/storage.
import { inMemoryRecordStore, keySpace } from "@use-crux/core/storage";
import type { JsonObject, RecordStore } from "@use-crux/core/storage";
const records = inMemoryRecordStore();
await records.put("plan:abc", { title: "My Plan", version: 1 });
const plan = await records.get("plan:abc");Use RecordStore for memory records, cache entries, flow snapshots, plans, tasks, blackboards, workspace metadata, and other structured Crux state. Use VectorStore for similarity search and AssetStore for binary or oversized payloads.
Interface
interface RecordStore<T extends JsonObject = JsonObject> {
get(key: string): Promise<T | null>;
getMany?(keys: readonly string[]): Promise<readonly (T | null)[]>;
put(key: string, value: T, options?: { ttlMs?: number }): Promise<void>;
putMany?(
entries: readonly { key: string; value: T; options?: { ttlMs?: number } }[],
): Promise<void>;
create(key: string, value: T, options?: { ttlMs?: number }): Promise<boolean>;
delete(key: string): Promise<void>;
deleteMany?(keys: readonly string[]): Promise<void>;
list(prefix: string, options?: RecordListOptions): Promise<RecordPage<T>>;
scan?(
prefix: string,
options?: Omit<RecordListOptions, "cursor">,
): AsyncIterable<RecordEntry<T>>;
watch?(prefix: string, callback: (event: RecordEvent<T>) => void): () => void;
capabilities(): RecordStoreCapabilities;
}put() creates or replaces a record. create() is the atomic insert primitive and returns false when an active record already exists.
Capabilities
type RecordStoreCapabilities = {
ttl: "native" | "lazy" | false;
filter: "native" | "scan" | false;
watch: boolean;
batch: boolean;
};Adapters must report the behavior they actually provide. Passing { ttlMs } to a store with ttl: false throws StorageError code ttl_unsupported.
Filters
Filters are exact top-level scalar equality:
await records.list("docs:", {
filter: { namespace: "public", active: true, deletedAt: null },
});Unsupported filter values must throw StorageError code invalid_filter; adapters should not silently drop unsupported clauses.
Watches
watch(prefix, callback) emits put and delete events when a backend supports live notifications:
records.watch?.("plan:", (event) => {
if (event.type === "put") {
console.log("Updated:", event.key, event.value);
} else {
console.log("Deleted:", event.key);
}
});Keyspace
keySpace centralizes Crux record-key prefixes used by core primitives:
const key = keySpace.plan.key("abc");
const page = await records.list(keySpace.plan.prefix);Implementations
| Store | Package | Use case |
|---|---|---|
inMemoryRecordStore() | @use-crux/core/storage | Tests and local examples |
convexRecordStore() | @use-crux/convex | Convex-backed records |
upstashRedisRecordStore() | @use-crux/upstash | Upstash Redis-backed records |
Bundle records with vectors and assets through storage({ records, vectors, assets }) when a primitive needs multiple capabilities.
Related
- Guide: RecordStore
- Reference: Storage interfaces
- Reference: Convex storage
- Reference: Upstash storage