1---
2title: D1
3description: Cloudflare D1 is serverless SQLite — create a database, apply migrations on deploy, and query it from a Worker through a typed binding.
4---
5
6D1 is Cloudflare's serverless SQLite database. There's nothing to
7provision or scale — you get a SQL database addressed by name, with
8reads served close to your users when read replication is enabled.
9
10Reach for D1 when your app needs relational data without operating a
11database: user tables, content, app state queried with plain SQL. If
12you need Postgres or MySQL (or already have one), front it with
13[Hyperdrive](/cloudflare/data/hyperdrive) instead.
14
15## Create a database
16
17A database is one resource declaration:
18
19```typescript
20// src/database.ts
21import * as Cloudflare from "alchemy/Cloudflare";
22
23export const Database = Cloudflare.D1.Database("Database");
24```
25
26Yield it inside your Stack like any other resource:
27
28```typescript
29// alchemy.run.ts
30import * as Alchemy from "alchemy";
31import * as Cloudflare from "alchemy/Cloudflare";
32import * as Effect from "effect/Effect";
33import { Database } from "./src/database.ts";
34
35export default Alchemy.Stack(
36 "MyApp",
37 {
38 providers: Cloudflare.providers(),
39 state: Cloudflare.state(),
40 },
41 Effect.gen(function* () {
42 const db = yield* Database;
43
44 return { databaseName: db.databaseName };
45 }),
46);
47```
48
49## Migrations
50
51Point `migrations` at a folder of `.sql` files. Files are sorted
52by numeric prefix (`0001_`, `0002_`, …) and applied in order as part
53of every deploy; already-applied migrations are skipped:
54
55```typescript
56const db = yield* Cloudflare.D1.Database("my-db", {
57 migrations: "./migrations",
58});
59```
60
61Bookkeeping lives in Alchemy's `__alchemy_migrations` table. A
62database previously migrated with `wrangler d1 migrations apply` or
63`drizzle-kit migrate` is
64[adopted automatically](/sql/effect-sql/migrations#adopting-an-existing-database):
65the old tool's applied history is copied in once and its table is
66frozen — never written, never dropped. D1 has no transactions over
67HTTP, so each migration and its bookkeeping row are sent as one
68batched query, matching wrangler's own behavior.
69
70The migration files themselves don't have to be hand-written —
71[Drizzle.Schema](/sql/drizzle/migrations) can generate them from a
72Drizzle schema on deploy. [Drizzle on D1](/cloudflare/data/d1-drizzle)
73walks through the whole flow: schema → generated migrations → typed
74queries from the Worker.
75
76## Query from a Worker
77
78Bind the database into a Worker with `Cloudflare.D1.QueryDatabase`
79and provide the `QueryDatabaseBinding` layer. The client exposes
80`prepare`, `exec`, `batch`, and `raw`; prepared statements execute
81with `all`, `first`, or `run`:
82
83```typescript
84// src/worker.ts
85import * as Cloudflare from "alchemy/Cloudflare";
86import * as Effect from "effect/Effect";
87import * as HttpServerResponse from "effect/http/HttpServerResponse";
88import { Database } from "./database.ts";
89
90export default Cloudflare.Worker(
91 "Worker",
92 { main: import.meta.url },
93 Effect.gen(function* () {
94 // Construction: register the binding, get a typed client
95 const db = yield* Cloudflare.D1.QueryDatabase(Database);
96
97 return {
98 fetch: Effect.gen(function* () {
99 // runtime: run queries
100 const results = yield* db
101 .prepare("SELECT * FROM users WHERE id = ?")
102 .bind(1)
103 .all();
104 return yield* HttpServerResponse.json(results);
105 }),
106 };
107 }).pipe(Effect.provide(Cloudflare.D1.QueryDatabaseBinding)),
108);
109```
110
111`prepare` and `bind` are synchronous plan builders — only the
112executors (`all`, `first`, `run`, `raw`) round-trip to the database,
113and those return Effects. For libraries that want the native
114`D1Database` object (e.g. Better Auth), use the client's `raw`
115escape hatch.
116
117## SQL with `@effect/sql-d1`
118
119Prefer tagged-template SQL over hand-written prepared statements?
120`SQL.D1` (from `alchemy/SQL/D1` — each low-level effect-sql client
121lives on its own subpath) wraps the same binding in an
122`@effect/sql-d1` `D1Client` —
123interpolated values are parameterized, every query is an Effect, and
124failures surface as typed `SqlError`s:
125
126```typescript
127import * as SQL from "alchemy/SQL/D1";
128
129Effect.gen(function* () {
130 const d1 = yield* Cloudflare.D1.QueryDatabase(Database);
131 const sql = yield* SQL.D1(d1);
132
133 return {
134 fetch: Effect.gen(function* () {
135 const users = yield* sql`SELECT * FROM users WHERE id = ${id}`;
136 return yield* HttpServerResponse.json({ users });
137 }),
138 };
139}).pipe(Effect.provide(Cloudflare.D1.QueryDatabaseBinding));
140```
141
142The client is built lazily on the first query and memoized per
143execution — a `fetch`/`queue`/`scheduled` event, a Durable Object
144call, or a Workflow run — then torn down when the event settles.
145
146`D1Client` implements Effect's generic `SqlClient` interface, so
147services written against `SqlClient.SqlClient` run on D1 unchanged.
148Provide both tags with the layer form:
149
150```typescript
151import * as SqlClient from "effect/sql/SqlClient";
152
153const makeApp = Effect.gen(function* () {
154 const sql = yield* SqlClient.SqlClient;
155 // ... services that only know about SqlClient
156});
157
158const app = yield* makeApp.pipe(
159 Effect.provide(SQL.D1Layer(d1)),
160);
161```
162
163The complete runnable project is
164[`cloudflare-effect-sql-d1`](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-effect-sql-d1).
165The full client API, the Layer pattern, and the lifecycle contract
166live in the SQL hub — [Effect SQL: D1](/sql/effect-sql/d1) and
167[Connection lifecycle](/sql/effect-sql/lifecycle). For typed schemas
168and an ORM on top of the same binding, see
169[Drizzle on D1](/cloudflare/data/d1-drizzle).
170
171## Where next
172
173Guides:
174
175- [Drizzle on D1](/cloudflare/data/d1-drizzle) — typed schemas,
176 generated migrations, and the `effect-d1` driver on this database.
177- [`cloudflare-effect-sql-d1`](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-effect-sql-d1)
178 — the runnable `@effect/sql-d1` example.
179
180Related:
181
182- [Workers](/cloudflare/compute/workers) — where your queries run.
183- [Hyperdrive](/cloudflare/data/hyperdrive) — for external Postgres/MySQL
184 instead of SQLite.
185
186Reference:
187
188- [Database API reference](/providers/cloudflare/d1/database)
189- [QueryDatabase API reference](/providers/cloudflare/d1/querydatabase)