Cloudflare D1
Provision D1 with Alchemy v2 and compose direct Drizzle storage.
D1 is the managed SQLite-compatible database bound to a Worker as D1Database. It uses the SQLite migration and generated Drizzle catalog, but its remote binding and batch semantics require D1-specific runtime implementations.
Install
bun add @effect-auth/core@0.1.0-alpha.19 effect@4.0.0-beta.98 drizzle-orm@1.0.0-rc.4 @effect/sql-d1@4.0.0-beta.98 alchemy@2.0.0-beta.63
bun add -d @cloudflare/workers-typesPin versions tested by the installed effect-auth release.
Runtime support
DrizzleD1SqliteAuthStorageLayer() composes all 42 maintained storage ports and requires one DrizzleD1Database service. Provide DrizzleD1DatabaseLive(binding) once to that Layer and to application repositories so the complete Worker graph shares one database capability. D1 commit stores retain native batches where a multi-write invariant requires them.
Applications that intentionally install only the account/password slice can continue to use D1SqliteAccountAuthStorageLive(binding, database). Individual focused layer and layerNoDeps exports remain available for selective feature graphs.
First-party durable direct support covers all 42 ports tracked in the project plan, including focused audit-log storage with native atomic batch insertion, atomic domain-verification transitions, bounded security-timeline keyset reads, permission authorization checks, and delegated-access stores with native atomic supersession.
Do not substitute a local SQLite transaction for D1 atomic behavior, and do not use memory storage for production.
Migrations
| D1 state | Migration source |
|---|---|
| New and empty | Generate a selective baseline with database=sqlite through the Schema Generator API |
| Existing effect-auth ledger | Apply only reviewed unapplied authStorageMigrations entries |
| Incompatible earlier alpha | Reset non-production data or create a reviewed one-off migration |
curl "https://effect-auth.itsbroly.com/api/generator/v1/artifacts/migration-sql?database=sqlite&features=password,totp&layout=module"Commit generated SQL under migrations/ and apply it during deployment. Never apply a fresh baseline to an existing database.
Provision with Alchemy
import * as Alchemy from "alchemy";
import * as Cloudflare from "alchemy/Cloudflare";
import * as Effect from "effect/Effect";
export const Database = Cloudflare.D1.Database("AuthDatabase", {
migrationsDir: "./migrations",
});
export const AuthWorker = Cloudflare.Worker("AuthWorker", {
main: "./src/worker.ts",
compatibility: { flags: ["nodejs_compat"] },
env: { DB: Database },
});
export default Alchemy.Stack(
"EffectAuth",
{ providers: Cloudflare.providers(), state: Cloudflare.state() },
Effect.gen(function* () {
const database = yield* Database;
const worker = yield* AuthWorker;
return {
databaseId: database.databaseId,
workerUrl: worker.url.as<string>(),
};
})
);Keep one database binding and database Layer per Worker scope. Never issue app-level BEGIN/COMMIT across separate binding calls. Test local workerd behavior and a deployed non-production database, including races and unknown commit-state failures.