Skip to content

CloudflareD1

Source: packages/better-auth/src/CloudflareD1.ts Kind: Layer · Provides: BetterAuth.Database

Cloudflare D1 database layer for Better Auth.

At runtime the Worker reaches the database through its native D1 binding (the layer bakes in QueryDatabaseBinding). At deploy time, schema migrations run over the D1 HTTP query API — which also transparently targets the local D1 simulator under alchemy dev.

Provide the layer on the Worker impl effect that yields BetterAuth. The database resource can be referenced from module scope — the layer accepts the resource or its Effect.

import { BetterAuth } from "@alchemy.run/better-auth";
import { CloudflareD1 } from "@alchemy.run/better-auth/CloudflareD1";
import * as Cloudflare from "alchemy/Cloudflare";
export const AuthDb = Cloudflare.D1.Database("AuthDb");
export default class Api extends Cloudflare.Worker<Api>()(
"Api",
{ main: import.meta.url, compatibility: { flags: ["nodejs_compat"] } },
Effect.gen(function* () {
const auth = yield* BetterAuth({
basePath: "/auth",
emailAndPassword: { enabled: true },
});
return {
fetch: Effect.gen(function* () {
const request = yield* HttpServerRequest;
if (request.url.startsWith("/auth")) {
return yield* auth.fetch;
}
const session = yield* auth.getSession();
return yield* HttpServerResponse.json({ user: session?.user ?? null });
}),
};
}).pipe(Effect.provide(CloudflareD1(AuthDb))),
) {}

The schema migration Action connects over the D1 HTTP API at deploy time — the Worker’s native binding is never used outside the deployed runtime, and no migration code ships in the Worker bundle.

const auth = yield* BetterAuth({
migrate: false, // manage the schema yourself
emailAndPassword: { enabled: true },
});