---
title: "Cloudflare D1"
url: "https://effect-auth.itsbroly.com/storage/cloudflare-d1/"
description: "Provision D1 with Alchemy v2 and connect effect-auth through Effect-QB or Drizzle."
---





Cloudflare D1 is the **storage/database** in this setup. It is a managed, SQLite-compatible database exposed to a Worker through a `D1Database` binding. **Effect-QB and Drizzle are alternative adapter paths** that connect effect-auth's storage services to that same database; they are not databases and do not provision D1.

Choose one adapter. Effect-QB is the direct path and has fewer dependencies. Choose Drizzle when the application already uses the Effect-native Drizzle D1 integration. Both paths use the same effect-auth schema and migrations.

```text
Auth program
     |
     v
Effect storage services
     |
     +---- Effect-QB adapter ----+
     |                           |
     +---- Drizzle adapter ------+--> Worker DB binding --> Cloudflare D1
```

For broader choices, see [Storage Overview](/storage/), [SQLite](/storage/sqlite/), or [Custom Database](/storage/custom-database/). For a complete Worker application, start with the [Quick Start](/quick-start/).

## Install [#install]

Install the shared runtime, infrastructure, and Worker types, then add exactly one adapter's dependencies.

```sh
bun add @effect-auth/core effect alchemy@2.0.0-beta.61
bun add -d @cloudflare/workers-types
```

<Tabs items="[&#x22;Effect-QB&#x22;, &#x22;Drizzle&#x22;]">
  <Tab value="Effect-QB">
    ```sh
    bun add effect-qb@4.0.0-beta.92
    ```
  </Tab>

  <Tab value="Drizzle">
    ```sh
    bun add drizzle-orm@1.0.0-rc.4 @effect/sql-d1@4.0.0-beta.93
    ```
  </Tab>
</Tabs>

Keep Effect ecosystem peer versions compatible with the installed effect-auth release. Alchemy v2 is pre-release here, so pin it and review upgrades.

## Export the migrations [#export-the-migrations]

effect-auth publishes the ordered SQLite migration stream through a public module. Export it to files that Alchemy can apply and commit those files with the application:

```ts title="scripts/write-auth-migrations.ts"
import { mkdir } from "node:fs/promises";
import { authStorageMigrations } from "@effect-auth/core/StorageMigrations";

await mkdir("migrations", { recursive: true });

for (const migration of authStorageMigrations) {
  await Bun.write(`migrations/${migration.id}.sql`, migration.sql);
}
```

```sh
bun scripts/write-auth-migrations.ts
```

Run this again when upgrading effect-auth and review the SQL diff. Migrations are shared by both adapters: Drizzle users do not recreate or push the auth schema with Drizzle Kit.

## Provision D1 [#provision-d1]

The D1 resource, binding, and migration lifecycle are common to both adapter paths:

```ts title="alchemy.run.ts"
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 Worker = Cloudflare.Worker("AuthWorker", {
  main: "./src/worker.ts",
  env: { DB: Database },
});

export default Alchemy.Stack(
  "EffectAuth",
  { providers: Cloudflare.providers(), state: Cloudflare.state() },
  Effect.gen(function* () {
    const database = yield* Database;
    const worker = yield* Worker;
    return {
      databaseId: database.databaseId,
      workerUrl: worker.url.as<string>(),
    };
  })
);
```

`Cloudflare.D1.Database` creates the database. Passing that resource as `Worker.env.DB` creates the binding whose runtime value is `D1Database`. Alchemy applies new `.sql` files from `migrationsDir`; append new migrations rather than editing files already applied to an environment.

## Provide the storage layer [#provide-the-storage-layer]

Create the layer from the Worker's binding and provide it to the auth composition.

<Tabs items="[&#x22;Effect-QB&#x22;, &#x22;Drizzle&#x22;]">
  <Tab value="Effect-QB">
    ```ts title="src/storage.ts"
    import { D1EffectQbSqliteAuthStorageLive } from "@effect-auth/core/EffectQbSqliteStorage";

    export interface Env {
      readonly DB: D1Database;
    }

    export const makeAuthStorageLive = (env: Env) =>
      D1EffectQbSqliteAuthStorageLive(env.DB);
    ```

    This adapter executes Effect-QB queries directly against the D1 binding.
  </Tab>

  <Tab value="Drizzle">
    ```ts title="src/storage.ts"
    import { DrizzleD1SqliteAuthStorageLive } from "@effect-auth/core/DrizzleD1SqliteStorage";

    export interface Env {
      readonly DB: D1Database;
    }

    export const makeAuthStorageLive = (env: Env) =>
      DrizzleD1SqliteAuthStorageLive(env.DB);
    ```

    This constructor creates the Effect-native Drizzle D1 integration. Do not substitute the promise-based `drizzle-orm/d1` driver.
  </Tab>
</Tabs>

## Lifecycle [#lifecycle]

Minimal scripts are sufficient:

```json title="package.json"
{
  "type": "module",
  "scripts": {
    "dev": "alchemy dev",
    "deploy": "alchemy deploy",
    "destroy": "alchemy destroy"
  }
}
```

Make `CLOUDFLARE_API_TOKEN` available, run `bun run dev` for a development stack, and run `bun run deploy` for the selected production stage. Use separate D1 databases per environment. `bun run destroy` deletes managed resources and must be protected for production.

## D1 caveats [#d1-caveats]

* Apply migrations during deployment, never on each request. Back up production data before schema changes.
* D1 binding calls are not a connection-scoped transaction. Do not wrap separate calls in `BEGIN`, `COMMIT`, or `SAVEPOINT`; the adapters rely on constraints and conditional statements for atomic state changes.
* D1 has platform limits, serialized writes, and remote-service latency. Keep queries indexed, avoid unbounded database concurrency, and test against D1 rather than assuming local SQLite behavior.
* The normal `D1Database` adapter path does not expose D1 Sessions or replica bookmarks. Review Cloudflare's [D1 limits](https://developers.cloudflare.com/d1/platform/limits/) and [Workers Binding API](https://developers.cloudflare.com/d1/worker-api/) before enabling replication or setting throughput expectations.

