Skip to main content
skill CX Codex

d1-migration — Claude Code skill for Cloudflare D1 migrations

Walks Cloudflare D1 migrations through Drizzle: generate the SQL, read it for traps, apply to local and remote, unstick partial runs.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add jezweb/claude-skills --skill d1-migration -a claude-code
Open on GitHub

What d1-migration does

The skill lays out a migration order for D1. pnpm db:generate drops a new .sql file into drizzle/, and the next step is to actually read it. Drizzle sometimes turns a small schema change into a full table recreation: CREATE TABLE my_table_new, an INSERT INTO ... SELECT that pulls the new column from the old table, then DROP TABLE and RENAME. That insert fails, because the old table has no such column. When you are only adding columns, edit the file down to ALTER TABLE my_table ADD COLUMN before applying anything. Worth checking every generated file.

Applying happens twice: pnpm db:migrate:local and pnpm db:migrate:remote. Local-only runs are the usual source of "works here, breaks in production". Verification uses npx wrangler d1 execute DB_NAME --local --command "PRAGMA table_info(my_table)", then the same with --remote.

Stuck migrations get their own section. If a column landed but no row was written to d1_migrations, wrangler retries the migration and trips over the duplicate. The fix is a manual insert into d1_migrations, then a normal run for the rest. The skill also flags D1's parameter limit: large multi-row inserts fail quietly, so it batches them 10 rows at a time. New projects follow a different order — deploy the Worker, run npx wrangler d1 create, copy database_id into wrangler.jsonc, redeploy, then migrate.

What it helps with

  • generate a SQL migration from a Drizzle schema
  • catch table recreation in generated SQL before it runs
  • apply a migration to both local and remote D1
  • repair a migration that applied halfway and got stuck
  • chunk a large insert around D1's parameter limit

Example prompts for the agent

  • generate the migration and show me the SQL before applying it
  • the migration is stuck: the column exists but d1_migrations has no row for it
  • set up D1 for a new project and run migrations locally and remotely

How to install d1-migration

1Skills CLI (Vercel)

npx skills add jezweb/claude-skills --skill d1-migration -a claude-code

2Manually: put the files in place

~/.claude/skills/d1-migration/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/d1-migration/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about d1-migration

How do I install d1-migration in Claude?

Run npx skills add jezweb/claude-skills --skill d1-migration -a claude-code. The skill lands in ~/.claude/skills/d1-migration/SKILL.md.

What does d1-migration do?

It guides Cloudflare D1 migrations with Drizzle ORM: generate, inspect the SQL, apply to local and remote, verify with wrangler d1 execute.

Why does a D1 migration get stuck?

The column was added but never recorded in d1_migrations, so wrangler retries and hits the duplicate. Insert the row into d1_migrations manually, then run the remaining migrations.

Source text

SKILL.md · MIT
---
name: d1-migration
description: "Cloudflare D1 migration workflow: generate with Drizzle, inspect SQL for gotchas, apply to local and remote, fix stuck migrations, handle partial failures. Use when running migrations, fixing migration errors, or setting up D1 schemas."
compatibility: claude-code-only
---

# D1 Migration Workflow

Guided workflow for Cloudflare D1 database migrations using Drizzle ORM.

## Standard Migration Flow

### 1. Generate Migration

```bash
pnpm db:generate
```

This creates a new `.sql` file in `drizzle/` (or your configured migrations directory).

### 2. Inspect the SQL (CRITICAL)

**Always read the generated SQL before applying.** Drizzle sometimes generates destructive migrations for simple schema changes.

#### Red Flag: Table Recreation

If you see this pattern, the migration will likely fail:

```sql
CREATE TABLE `my_table_new` (...);
INSERT INTO `my_table_new` SELECT ..., `new_column`, ... FROM `my_table`;
--                                      ^^^ This column doesn't exist in old table!
DROP TABLE `my_table`;
ALTER TABLE `my_table_new` RENAME TO `my_table`;
```

**Cause**: Changing a column's `default` value in Drizzle schema triggers full table recreation. The INSERT SELECT references the new column from the old table.

**Fix**: If you're only adding new columns (no type/constraint changes on existing columns), simplify to:

```sql
ALTER TABLE `my_table` ADD COLUMN `new_column` TEXT DEFAULT 'value';
```

Edit the `.sql` file directly before applying.

### 3. Apply to Local

```bash
pnpm db:migrate:local
# or: npx wrangler d1 migrations apply DB_NAME --local
```

### 4. Apply to Remote

```bash
pnpm db:migrate:remote
# or: npx wrangler d1 migrations apply DB_NAME --remote
```

**Always apply to BOTH local and remote before testing.** Local-only migrations cause confusing "works locally, breaks in production" issues.

### 5. Verify

```bash
# Check local
npx wrangler d1 execute DB_NAME --local --command "PRAGMA table_info(my_table)"

# Check remote
npx wrangler d1 execute DB_NAME --remote --command "PRAGMA table_info(my_table)"
```

## Fixing Stuck Migrations

When a migration partially applied (e.g. column was added but migration wasn't recorded), wrangler retries it and fails on the duplicate column.

**Symptoms**: `pnpm db:migrate` errors on a migration that looks like it should be done. `PRAGMA table_info` shows the column exists.

### Diagnosis

```bash
# 1. Verify the column/table exists
npx wrangler d1 execute DB_NAME --remote \
  --command "PRAGMA table_info(my_table)"

# 2. Check what migrations are recorded
npx wrangler d1 execute DB_NAME --remote \
  --command "SELECT * FROM d1_migrations ORDER BY id"
```

### Fix

```bash
# 3. Manually record the stuck migration
npx wrangler d1 execute DB_NAME --remote \
  --command "INSERT INTO d1_migrations (name, applied_at) VALUES ('0013_my_migration.sql', datetime('now'))"

# 4. Run remaining migrations normally
pnpm db:migrate
```

### Prevention

- `CREATE TABLE IF NOT EXISTS` — safe to re-run
- `ALTER TABLE ADD COLUMN` — SQLite has no `IF NOT EXISTS` variant; check column existence first or use try/catch in application code
- **Always inspect generated SQL** before applying (Step 2 above)

## Bulk Insert Batching

D1's parameter limit causes silent failures with large multi-row INSERTs. Batch into chunks:

```typescript
const BATCH_SIZE = 10;
for (let i = 0; i < allRows.length; i += BATCH_SIZE) {
  const batch = allRows.slice(i, i + BATCH_SIZE);
  await db.insert(myTable).values(batch);
}
```

**Why**: D1 fails when rows x columns exceeds ~100-150 parameters.

## Column Naming

| Context | Convention | Example |
|---------|-----------|---------|
| Drizzle schema | camelCase | `caseNumber: text('case_number')` |
| Raw SQL queries | snake_case | `UPDATE cases SET case_number = ?` |
| API responses | Match SQL aliases | `SELECT case_number FROM cases` |

## New Project Setup

When creating a D1 database for a new project, follow this order:

1. **Deploy Worker first** — `npm run build && npx wrangler deploy`
2. **Create D1 database** — `npx wrangler d1 create project-name-db`
3. **Copy database_id** to `wrangler.jsonc` `d1_databases` binding
4. **Redeploy** — `npx wrangler deploy`
5. **Run migrations** — apply to both local and remote

© jezweb · MIT · jezweb/claude-skills

Similar skills

All skills for Data and databases →

From the same set60

design-review Reviews the visual quality of a web page or app and produces a findings report with screenshots. Use it when an interface looks off and the cause is hard to spot. CX Codex +1 1,400installs react-patterns A skill that checks React 19 components for waterfalls, bundle bloat and re-renders, and suggests composition fixes. Useful while writing or reviewing code. CX Codex +1 1,300installs react-native Performance guidelines and architecture patterns for React Native and Expo apps. Helps coding agents handle list virtualization, UI-thread animations, and navigation. CX Codex +1 1,300installs github-release Prepares a GitHub release by checking code for public publishing, creating a tag, and publishing through gh. Use it before making a repository public. CX Codex +1 1,300installs deep-research Does the digging before you write code: scans your own projects, rivals, forums and libraries, then hands back a research brief. CX Codex +1 1,200installs proposal-writer Drafts client proposals, scopes of work, and quotes for service businesses. It helps web developers, agencies, and consultants close deals with structured terms. CX Codex +1 1,200installs shopify-content Creates and updates Shopify store content through the Admin API or browser automation. Use it for pages, blog posts, menus, redirects, and SEO fields. CX Codex +1 1,200installs vite-flare-starter Clones the vite-flare-starter template and configures it into your own Cloudflare Workers app, with React, Hono, D1 and Drizzle, auth and Workers AI already in place. CX Codex +1 1,200installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor