Storage
Storage adapters manage conversation state persistence. FlowKit includes several storage options out of the box.
Built-in Storage Adapters
MemoryStorage
In-memory storage for development and single-server deployments.
import { MemoryStorage } from "@andresaya/flowkit";
const storage = new MemoryStorage();
const engine = new FlowEngine(flow, { llm: adapter, storage });Characteristics:
- ✅ No setup required
- ✅ Fast (in-memory)
- ❌ Data lost on restart
- ❌ Not shared across servers
FileStorage
JSON file-based storage for simple persistence.
import { FileStorage } from "@andresaya/flowkit";
const storage = new FileStorage({
directory: "./data/sessions", // Where to store files
pretty: true, // Pretty-print JSON (optional)
});
const engine = new FlowEngine(flow, { llm: adapter, storage });Characteristics:
- ✅ Persists across restarts
- ✅ No external dependencies
- ✅ Easy to inspect/debug
- ❌ Not suitable for high concurrency
- ❌ Not shared across servers
API:
// List all stored conversations
const conversations = await storage.list();
// Returns: ["session-1", "session-2", ...]
// Clean up old conversations (older than 24 hours)
await storage.cleanup(24 * 60 * 60 * 1000);RedisStorage
Distributed storage using Redis. Perfect for production multi-server deployments.
import { RedisStorage } from "@andresaya/flowkit";
import Redis from "ioredis"; // npm install ioredis
// Create Redis client
const redisClient = new Redis({
host: "localhost",
port: 6379,
password: "optional-password",
});
// Create storage with client
const storage = new RedisStorage({
client: redisClient, // Redis client instance (required)
prefix: "flowkit:", // Key prefix (default: "flowkit:")
ttl: 86400, // TTL in seconds (default: 24 hours)
});
const engine = new FlowEngine(flow, { llm: adapter, storage });Characteristics:
- ✅ Distributed/shared state
- ✅ Automatic expiration (TTL)
- ✅ High performance
- ⚠️ Requires Redis server
- ⚠️ Requires
ioredispackage:npm install ioredis
SQLiteStorage
Local SQL database storage. Great for single-server production deployments.
import { SQLiteStorage } from "@andresaya/flowkit";
import Database from "better-sqlite3"; // npm install better-sqlite3
// Create database instance
const db = new Database("./data/flowkit.db");
// Create storage with database
const storage = new SQLiteStorage({
db: db, // SQLite database instance (required)
table: "conversations", // Table name (default: "conversations")
});
const engine = new FlowEngine(flow, { llm: adapter, storage });
// Additional methods available:
storage.list(); // Get all conversation IDs
storage.clear(); // Delete all conversations
storage.count(); // Get number of conversationsCharacteristics:
- ✅ Persistent storage
- ✅ SQL query capabilities
- ✅ Single file, easy backups
- ⚠️ Single server only
- ⚠️ Requires
better-sqlite3package:npm install better-sqlite3
StorageAdapter Interface
Create custom storage for other databases or services.
interface StorageAdapter {
load(conversationId: string): Promise<ConversationState | null>;
save(state: ConversationState): Promise<void>;
delete(conversationId: string): Promise<void>;
}ConversationState Structure
interface ConversationState {
conversationId: string; // Conversation/session ID
currentStep: string; // Current step ID
slots: Record<string, JsonValue>; // Collected data
history: Array<{ role: "user" | "assistant"; content: string }>; // Chat history
ended: boolean; // Flow completed?
createdAt: number; // Creation timestamp
updatedAt: number; // Last update timestamp
}Custom Storage Examples
MongoDB Storage
import { MongoClient, Collection } from "mongodb";
import type { StorageAdapter, ConversationState } from "@andresaya/flowkit";
class MongoStorage implements StorageAdapter {
private collection: Collection;
constructor(uri: string, dbName: string) {
const client = new MongoClient(uri);
this.collection = client.db(dbName).collection("sessions");
this.collection.createIndex({ sessionId: 1 }, { unique: true });
this.collection.createIndex(
{ updatedAt: 1 },
{ expireAfterSeconds: 86400 } // Auto-expire after 24h
);
}
async get(sessionId: string): Promise<ConversationState | null> {
const doc = await this.collection.findOne({ sessionId });
return doc?.state || null;
}
async set(sessionId: string, state: ConversationState): Promise<void> {
await this.collection.updateOne(
{ sessionId },
{ $set: { state, updatedAt: new Date() } },
{ upsert: true }
);
}
async delete(sessionId: string): Promise<void> {
await this.collection.deleteOne({ sessionId });
}
}File-based Storage
import { readFile, writeFile, unlink, mkdir } from "fs/promises";
import { join } from "path";
import { StorageAdapter, ConversationState } from "@andresaya/flowkit";
class FileStorage implements StorageAdapter {
private dir: string;
constructor(directory: string = "./sessions") {
this.dir = directory;
mkdir(this.dir, { recursive: true });
}
private path(sessionId: string): string {
// Sanitize sessionId to prevent directory traversal
const safe = sessionId.replace(/[^a-zA-Z0-9-_]/g, "_");
return join(this.dir, `${safe}.json`);
}
async get(sessionId: string): Promise<ConversationState | null> {
try {
const data = await readFile(this.path(sessionId), "utf-8");
return JSON.parse(data);
} catch {
return null;
}
}
async set(sessionId: string, state: ConversationState): Promise<void> {
await writeFile(this.path(sessionId), JSON.stringify(state, null, 2));
}
async delete(sessionId: string): Promise<void> {
try {
await unlink(this.path(sessionId));
} catch {
// Ignore if file doesn't exist
}
}
}Accessing Stored Data
Use Engine methods to read/write state:
// Get all collected data
const slots = engine.getSlots(sessionId);
// { name: "John", email: "john@example.com" }
// Get specific value
const name = engine.getSlot(sessionId, "name");
// "John"
// Update a value
await engine.setSlot(sessionId, "verified", "true");
// Get conversation history
const history = engine.getHistory(sessionId);
// Check if flow ended
const ended = engine.isEnded(sessionId);
// Clear session
await engine.delete(sessionId);Tips
Use TTL/expiration - Don't let sessions live forever
Index session IDs - For faster lookups in databases
Handle errors gracefully - Storage can fail
Consider serialization - JSON.stringify/parse for objects
Security - Sanitize session IDs, encrypt sensitive data