Skip to main content
The StateAdapter interface defines the contract for managing persistent state in Chat SDK. State adapters handle thread subscriptions, distributed locking, and caching.

Built-in Implementations

Chat SDK provides three state adapter implementations:

Interface

interface StateAdapter {
  connect(): Promise<void>;
  disconnect(): Promise<void>;
  
  // Subscriptions
  subscribe(threadId: string): Promise<void>;
  unsubscribe(threadId: string): Promise<void>;
  isSubscribed(threadId: string): Promise<boolean>;
  
  // Locking
  acquireLock(threadId: string, ttlMs: number): Promise<Lock | null>;
  releaseLock(lock: Lock): Promise<void>;
  extendLock(lock: Lock, ttlMs: number): Promise<boolean>;
  
  // Caching
  get<T = unknown>(key: string): Promise<T | null>;
  set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void>;
  delete(key: string): Promise<void>;
}

interface Lock {
  threadId: string;
  token: string;
  expiresAt: number;
}

Methods

connect()

Establish connection to the state backend.
await state.connect();
Called automatically by Chat SDK during initialization.

disconnect()

Close connection to the state backend.
await state.disconnect();
Called automatically by Chat SDK during shutdown.

subscribe()

Subscribe to a thread to receive all future messages.
threadId
string
required
The thread ID to subscribe to
await state.subscribe("slack:C123ABC:1234567890.123456");
Subscriptions persist across restarts and are stored indefinitely until explicitly unsubscribed.

unsubscribe()

Unsubscribe from a thread.
threadId
string
required
The thread ID to unsubscribe from
await state.unsubscribe("slack:C123ABC:1234567890.123456");

isSubscribed()

Check if currently subscribed to a thread.
threadId
string
required
The thread ID to check
isSubscribed
boolean
Whether the thread is subscribed
const subscribed = await state.isSubscribed("slack:C123ABC:1234567890.123456");

acquireLock()

Acquire a distributed lock on a thread with a TTL.
threadId
string
required
The thread ID to lock
ttlMs
number
required
Time-to-live in milliseconds
lock
Lock | null
Lock object if acquired, null if already locked
const lock = await state.acquireLock(threadId, 5000); // 5 second lock
if (lock) {
  try {
    // Process message with exclusive access
  } finally {
    await state.releaseLock(lock);
  }
}
Locks prevent concurrent processing of messages in the same thread across multiple instances.

releaseLock()

Release a previously acquired lock.
lock
Lock
required
The lock object to release
await state.releaseLock(lock);

extendLock()

Extend the TTL of an existing lock.
lock
Lock
required
The lock object to extend
ttlMs
number
required
New time-to-live in milliseconds
success
boolean
Whether the lock was successfully extended
const extended = await state.extendLock(lock, 5000);
if (!extended) {
  // Lock expired or was released
}

get()

Retrieve a cached value by key.
key
string
required
Cache key
value
T | null
Cached value or null if not found
const data = await state.get<{ userId: string }>("user:123");

set()

Store a value in cache with optional TTL.
key
string
required
Cache key
value
T
required
Value to cache
ttlMs
number
Optional time-to-live in milliseconds
await state.set("user:123", { userId: "U123", name: "Alice" }, 3600000); // 1 hour

delete()

Delete a cached value by key.
key
string
required
Cache key to delete
await state.delete("user:123");

Implementing a Custom Adapter

To create a custom state adapter:
import type { StateAdapter, Lock } from "chat";

export class MyStateAdapter implements StateAdapter {
  async connect(): Promise<void> {
    // Connect to your backend
  }

  async disconnect(): Promise<void> {
    // Close connections
  }

  async subscribe(threadId: string): Promise<void> {
    // Store subscription
  }

  async unsubscribe(threadId: string): Promise<void> {
    // Remove subscription
  }

  async isSubscribed(threadId: string): Promise<boolean> {
    // Check if subscribed
    return false;
  }

  async acquireLock(threadId: string, ttlMs: number): Promise<Lock | null> {
    // Implement distributed locking
    return null;
  }

  async releaseLock(lock: Lock): Promise<void> {
    // Release the lock
  }

  async extendLock(lock: Lock, ttlMs: number): Promise<boolean> {
    // Extend lock TTL
    return false;
  }

  async get<T>(key: string): Promise<T | null> {
    // Retrieve cached value
    return null;
  }

  async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {
    // Store cached value
  }

  async delete(key: string): Promise<void> {
    // Delete cached value
  }
}

See Also