A Worker with a separate SQLite users table for each named Durable Object, using Drizzle and checked-in SQL.
Start at the repository root; run subsequent commands from the example directory.
pnpm install
cd examples/cloudflare-durable-object-sql
pnpm devUse the Worker URL printed by Alchemy:
URL='http://localhost:<port>'
curl -X POST "$URL/objects/team-a/users" \
-H 'content-type: application/json' \
-d '{"name":"Ada"}'
# {"user":{"id":1,"name":"Ada"}}
curl "$URL/objects/team-a/users"
# {"users":[{"id":1,"name":"Ada"}]}
curl "$URL/objects/team-b/users"
# {"users":[]}Configure Cloudflare credentials if needed, then deploy:
pnpm exec alchemy profile edit --add Cloudflare
pnpm deployKeep .alchemy for subsequent deployments and cleanup.
The example already includes drizzle/20260919000000_create_users/migration.sql:
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);src/Users.ts captures the SQL during construction and applies it when each object activates:
Effect.gen(function* () {
const migrations = yield* Cloudflare.SqlMigrations("./drizzle");
return Effect.gen(function* () {
const db = yield* Drizzle.DurableObject({ migrations, relations });
return { listUsers: () => db.select().from(users) };
});
});Add new SQL files with your schema changes; keep applied files unchanged. Restart pnpm dev after SQL edits.
git add src/schema.ts drizzle
git commit -m "Add schema migration"
pnpm deployconst migrations = yield* Cloudflare.SqlMigrations({
dir: "./drizzle",
table: "app_migrations", // default: __alchemy_migrations
});In the inner instance Effect:
yield* migrations.apply().pipe(Effect.orDie);See the migration guide for drizzle-kit setup, rollback behavior, and existing migration history.
Uses real Cloudflare resources and destroys the test deployment afterward.
pnpm testDeletes the Worker and all Durable Object data; keeps the SQL files in Git.
pnpm destroy