From 3176f74454ca87240c4cd18e9c5057508a039168 Mon Sep 17 00:00:00 2001 From: Ivory Date: Tue, 30 Jun 2026 14:25:51 -0500 Subject: [PATCH 1/2] docs(d1): add ORM migration workflow and Drizzle integration warnings --- .../docs/d1/reference/community-projects.mdx | 10 ++++++++ src/content/docs/d1/reference/migrations.mdx | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/content/docs/d1/reference/community-projects.mdx b/src/content/docs/d1/reference/community-projects.mdx index a095bb554ff..11f8a4f5611 100644 --- a/src/content/docs/d1/reference/community-projects.mdx +++ b/src/content/docs/d1/reference/community-projects.mdx @@ -62,6 +62,16 @@ Drizzle is a headless TypeScript ORM with a head which runs on Node, Bun and Den * [GitHub](https://github.com/drizzle-team/drizzle-orm) * [D1 example](https://orm.drizzle.team/docs/connect-cloudflare-d1) +:::caution[Using Drizzle migrations with D1] + +When using Drizzle with D1, be aware of the following: + +- **`drizzle-kit migrate` does not work with D1.** Drizzle cannot apply migrations to D1 directly because D1 requires Cloudflare's API for writes. Use `drizzle-kit generate` to create the SQL migration files, then apply them with `wrangler d1 migrations apply`. Refer to [Nested migration layouts](/d1/reference/migrations/#nested-migration-layouts) for how to configure Wrangler to read Drizzle's output folder. +- **`drizzle-kit generate` will try to drop tables not in your schema file.** Drizzle assumes it owns every table in the database. Tables that exist in the database but are not defined in your TypeScript schema — including Wrangler's `d1_migrations` tracking table and Cloudflare's internal `_cf_KV` table — will appear as `DROP TABLE` statements in generated migrations. Always review generated SQL before applying. To prevent this, add `tablesFilter: ["!d1_migrations", "!_cf_KV", "!sqlite_sequence"]` to your `drizzle.config.ts`. +- **Drizzle and Wrangler track migrations separately.** Wrangler records applied migrations in the `d1_migrations` table. Drizzle tracks them in `drizzle/meta/_journal.json`. These two systems do not communicate. If you use both, let Wrangler handle tracking and use Drizzle only for SQL generation. + +::: + ### workers-qb `workers-qb` is a zero-dependency query builder that provides a simple standardized interface while keeping the benefits and speed of using raw queries over a traditional ORM. While not intended to provide ORM-like functionality, `workers-qb` makes it easier to interact with your database from code for direct SQL access. diff --git a/src/content/docs/d1/reference/migrations.mdx b/src/content/docs/d1/reference/migrations.mdx index 750ef2da17d..69662d681da 100644 --- a/src/content/docs/d1/reference/migrations.mdx +++ b/src/content/docs/d1/reference/migrations.mdx @@ -86,6 +86,29 @@ The pattern is a standard glob — `*` matches one path segment, `**` matches an `wrangler d1 migrations create` only writes top-level files inside `migrations_dir`, so if your `migrations_pattern` only matches nested files (as with the Drizzle layout), generate new migrations using your ORM's command (for example, `drizzle-kit generate`) instead. +## Using an ORM for migrations + +You can use an ORM like [Drizzle](https://orm.drizzle.team/) or [Prisma](https://www.prisma.io/) to generate migration SQL instead of writing it by hand. With an ORM, you define your schema in TypeScript (or a schema file), and the ORM's CLI detects changes and outputs `.sql` migration files. + +ORMs cannot apply migrations to D1 directly. D1 requires Cloudflare's API for writes, so you must use Wrangler to apply the generated SQL. The workflow is: + +1. Define or edit your schema in TypeScript (or your ORM's schema format). +2. Run your ORM's migration generation command (for example, `drizzle-kit generate`) to produce a `.sql` file. +3. Review the generated SQL. ORMs may generate destructive statements for tables they do not manage. For example, Drizzle assumes it owns every table in the database — any table not defined in your schema file will appear as a `DROP TABLE` statement. +4. Apply the migration with `wrangler d1 migrations apply`. + +To connect an ORM's migration output folder to Wrangler, use `migrations_dir` and `migrations_pattern` in your [Wrangler configuration](#wrangler-customizations). Refer to [Nested migration layouts](#nested-migration-layouts) for an example using Drizzle's subdirectory layout. + +### When to use an ORM vs. raw SQL + +Raw SQL migrations with Wrangler work well when you have a small number of tables, your queries are straightforward, you are comfortable writing SQL, or you are the only developer on the project. + +An ORM starts to pay off when your schema grows to many tables with foreign key relationships, you build complex queries with joins across multiple tables and want compile-time type safety, multiple developers touch the schema and need a single TypeScript definition as the source of truth, or you want editor autocompletion when writing queries. + +Either way, always review migration SQL before applying to production. + +For a list of ORMs, query builders, and tools that work with D1, refer to [Community projects](/d1/reference/community-projects/). + ## Foreign key constraints When applying a migration, you may need to temporarily disable [foreign key constraints](/d1/sql-api/foreign-keys/). To do so, call `PRAGMA defer_foreign_keys = true` before making changes that would violate foreign keys. From 30fe957ed38dd5258477eba49fb7bc4fa0cbcba7 Mon Sep 17 00:00:00 2001 From: Ivory Date: Tue, 30 Jun 2026 15:06:28 -0500 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20review=20comments=20?= =?UTF-8?q?=E2=80=94=20heading=20gerunds,=20run-on=20sentence,=20monospace?= =?UTF-8?q?=20tool=20names,=20tablesFilter=20backticks,=20migrations=5Ftab?= =?UTF-8?q?le=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/d1/reference/community-projects.mdx | 6 +++--- src/content/docs/d1/reference/migrations.mdx | 21 ++++++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/content/docs/d1/reference/community-projects.mdx b/src/content/docs/d1/reference/community-projects.mdx index 11f8a4f5611..7a892eae6dd 100644 --- a/src/content/docs/d1/reference/community-projects.mdx +++ b/src/content/docs/d1/reference/community-projects.mdx @@ -66,9 +66,9 @@ Drizzle is a headless TypeScript ORM with a head which runs on Node, Bun and Den When using Drizzle with D1, be aware of the following: -- **`drizzle-kit migrate` does not work with D1.** Drizzle cannot apply migrations to D1 directly because D1 requires Cloudflare's API for writes. Use `drizzle-kit generate` to create the SQL migration files, then apply them with `wrangler d1 migrations apply`. Refer to [Nested migration layouts](/d1/reference/migrations/#nested-migration-layouts) for how to configure Wrangler to read Drizzle's output folder. -- **`drizzle-kit generate` will try to drop tables not in your schema file.** Drizzle assumes it owns every table in the database. Tables that exist in the database but are not defined in your TypeScript schema — including Wrangler's `d1_migrations` tracking table and Cloudflare's internal `_cf_KV` table — will appear as `DROP TABLE` statements in generated migrations. Always review generated SQL before applying. To prevent this, add `tablesFilter: ["!d1_migrations", "!_cf_KV", "!sqlite_sequence"]` to your `drizzle.config.ts`. -- **Drizzle and Wrangler track migrations separately.** Wrangler records applied migrations in the `d1_migrations` table. Drizzle tracks them in `drizzle/meta/_journal.json`. These two systems do not communicate. If you use both, let Wrangler handle tracking and use Drizzle only for SQL generation. +- **`drizzle-kit migrate` does not work with D1.** Drizzle cannot apply migrations to D1 directly because D1 requires Cloudflare's API for writes. Use `drizzle-kit generate` to create the SQL migration files, then apply them with `wrangler d1 migrations apply`. Refer to [Nested migration layouts](/d1/reference/migrations/#nested-migration-layouts) for how to configure `wrangler` to read Drizzle's output folder. +- **`drizzle-kit generate` will try to drop tables not in your schema file.** Drizzle assumes it owns every table in the database. Tables that exist in the database but are not defined in your TypeScript schema — including the `d1_migrations` tracking table and Cloudflare's internal `_cf_KV` table — will appear as `DROP TABLE` statements in generated migrations. Always review generated SQL before applying. To prevent this, add `tablesFilter` to your `drizzle.config.ts`: `["!d1_migrations", "!_cf_KV", "!sqlite_sequence"]`. If you customized the tracking table name via `migrations_table` in your Wrangler configuration, use that name instead of `d1_migrations`. +- **Drizzle and `wrangler` track migrations separately.** `wrangler` records applied migrations in the `d1_migrations` table. Drizzle tracks them in `drizzle/meta/_journal.json`. These two systems do not communicate. If you use both, let `wrangler` handle tracking and use Drizzle only for SQL generation. ::: diff --git a/src/content/docs/d1/reference/migrations.mdx b/src/content/docs/d1/reference/migrations.mdx index 69662d681da..86637d8a2a4 100644 --- a/src/content/docs/d1/reference/migrations.mdx +++ b/src/content/docs/d1/reference/migrations.mdx @@ -86,24 +86,29 @@ The pattern is a standard glob — `*` matches one path segment, `**` matches an `wrangler d1 migrations create` only writes top-level files inside `migrations_dir`, so if your `migrations_pattern` only matches nested files (as with the Drizzle layout), generate new migrations using your ORM's command (for example, `drizzle-kit generate`) instead. -## Using an ORM for migrations +## Generate migrations with an ORM -You can use an ORM like [Drizzle](https://orm.drizzle.team/) or [Prisma](https://www.prisma.io/) to generate migration SQL instead of writing it by hand. With an ORM, you define your schema in TypeScript (or a schema file), and the ORM's CLI detects changes and outputs `.sql` migration files. +You can use an object-relational mapping (ORM) tool like [Drizzle](https://orm.drizzle.team/) or [Prisma](https://www.prisma.io/) to generate migration SQL instead of writing it by hand. With an ORM, you define your schema in TypeScript (or a schema file), and the ORM's CLI detects changes and outputs `.sql` migration files. -ORMs cannot apply migrations to D1 directly. D1 requires Cloudflare's API for writes, so you must use Wrangler to apply the generated SQL. The workflow is: +ORMs cannot apply migrations to D1 directly. D1 requires Cloudflare's API for writes, so you must use `wrangler` to apply the generated SQL. The workflow is: 1. Define or edit your schema in TypeScript (or your ORM's schema format). 2. Run your ORM's migration generation command (for example, `drizzle-kit generate`) to produce a `.sql` file. -3. Review the generated SQL. ORMs may generate destructive statements for tables they do not manage. For example, Drizzle assumes it owns every table in the database — any table not defined in your schema file will appear as a `DROP TABLE` statement. +3. Review the generated SQL for destructive statements. ORMs may generate `DROP TABLE` statements for tables they do not manage, such as the `d1_migrations` tracking table. 4. Apply the migration with `wrangler d1 migrations apply`. -To connect an ORM's migration output folder to Wrangler, use `migrations_dir` and `migrations_pattern` in your [Wrangler configuration](#wrangler-customizations). Refer to [Nested migration layouts](#nested-migration-layouts) for an example using Drizzle's subdirectory layout. +To connect an ORM's migration output folder to `wrangler`, use `migrations_dir` and `migrations_pattern` in your [Wrangler configuration](#wrangler-customizations). Refer to [Nested migration layouts](#nested-migration-layouts) for an example using Drizzle's subdirectory layout. -### When to use an ORM vs. raw SQL +### ORM vs. raw SQL -Raw SQL migrations with Wrangler work well when you have a small number of tables, your queries are straightforward, you are comfortable writing SQL, or you are the only developer on the project. +Raw SQL migrations with `wrangler` work well when you have a small number of tables, your queries are basic, you are comfortable writing SQL, or you are the only developer on the project. -An ORM starts to pay off when your schema grows to many tables with foreign key relationships, you build complex queries with joins across multiple tables and want compile-time type safety, multiple developers touch the schema and need a single TypeScript definition as the source of truth, or you want editor autocompletion when writing queries. +An ORM starts to pay off in the following situations: + +- Your schema grows to many tables with foreign key relationships. +- You build complex queries with joins across multiple tables and want compile-time type safety. +- Multiple developers touch the schema and need a single TypeScript definition as the source of truth. +- You want editor autocompletion when writing queries. Either way, always review migration SQL before applying to production.