Cloudflare Durable Objects

State adapter for Chat SDK backed by a SQLite Durable Object. Persistent subscriptions, distributed locking, queues, lists, and caching from inside Cloudflare Workers — no Redis, no database.

Community adapter. Not maintained by Vercel or Chat SDK contributors. For feature requests, bug reports, and support, file an issue on the adapter's repo.

Install

pnpm add chat chat-state-cloudflare-do

Quick start

src/index.ts
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { ChatStateDO, createCloudflareState } from "chat-state-cloudflare-do";

// Re-export the Durable Object class so Cloudflare can find it.
export { ChatStateDO };

export default {
  async fetch(request: Request, env: Env) {
    const bot = new Chat({
      userName: "my-bot",
      adapters: { slack: createSlackAdapter() },
      state: createCloudflareState({ namespace: env.CHAT_STATE }),
    });
    return bot.webhooks.slack(request);
  },
};

Wrangler configuration

Add the Durable Object binding and migration to your wrangler.jsonc (recommended) or wrangler.toml:

wrangler.jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "CHAT_STATE", "class_name": "ChatStateDO" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ChatStateDO"] }]
}
wrangler.toml
[durable_objects]
bindings = [
  { name = "CHAT_STATE", class_name = "ChatStateDO" }
]

[[migrations]]
tag = "v1"
new_sqlite_classes = ["ChatStateDO"]

Environment type

src/env.d.ts
import type { ChatStateDO } from "chat-state-cloudflare-do";

interface Env {
  CHAT_STATE: DurableObjectNamespace<ChatStateDO>;
}

Configuration

Prop

Type

Sharding

A single Durable Object handles roughly 500–1000 requests per second. For high-traffic bots, use shardKey to distribute load across multiple DO instances:

const state = createCloudflareState({
  namespace: env.CHAT_STATE,
  shardKey: (threadId) => threadId.split(":")[0], // one DO per platform
});

Locks, force-release, and queue operations are per-thread, so sharding by any prefix of the thread ID is safe. Cache and list operations (get / set / delete, appendToList / getList) always route to the default shard since their keys are not thread-scoped.

StrategyshardKeyDOs created
No sharding (default)1
Per platform(id) => id.split(":")[0]1 per platform
Per channel(id) => id.split(":").slice(0, 2).join(":")1 per channel

Architecture

The adapter uses a single Durable Object class (ChatStateDO) with five SQLite tables:

  • subscriptions — thread IDs the bot is subscribed to
  • locks — distributed locks with token-based ownership and TTL
  • cache — key-value pairs with optional TTL
  • queue — thread-scoped FIFO queue entries with TTL for concurrency strategies
  • lists — ordered list entries for persistent message history

All operations are single-threaded within a DO instance, which gives you distributed locking via DO atomicity rather than Lua scripts. Expired entries are cleaned up automatically via the Alarms API.

Each method call creates a fresh DO stub. Stubs are cheap (just a JS object) and the Cloudflare docs recommend creating new stubs rather than reusing them after errors.

Capabilities

  • Persistent subscriptions across deployments
  • Distributed locking via single-threaded DO atomicity
  • Lock force-release for Chat SDK lock conflict handling
  • Queue/debounce concurrency primitives
  • List-backed persistent message history
  • Key-value caching with TTL
  • Automatic TTL cleanup via Alarms
  • Optional sharding for high-traffic bots
  • Location hints for latency optimization
  • Zero external dependencies (no Redis, no database)

Production recommendations

  • Use Smart Placement to co-locate your Worker with the DO.
  • Monitor DO metrics in the Cloudflare dashboard.
  • Enable sharding if you expect more than ~500 req/s to a single DO instance.
  • Use locationHint to place the DO near your primary user base.

Feature support

Capabilities

FeatureSupported
Persistence
Multi-instance
Subscriptions
Distributed locking
Key-value caching
Lists
Queues
Automatic reconnect
Cluster supportSharding
Sentinel support
Key prefix namespacingshardKey

On this page