d1-migration
The D1 Migration Workflow skill guides developers through Cloudflare D1 database migrations using Drizzle ORM, covering generation, SQL inspection, local and remote application, and recovery from stuck migrations. Use this when generating migrations, fixing migration errors, dealing with partial failures, or setting up D1 database schemas in Cloudflare Workers projects.
git clone --depth 1 https://github.com/jezweb/claude-skills /tmp/d1-migration && cp -r /tmp/d1-migration/plugins/cloudflare/skills/d1-migration ~/.claude/skills/d1-migrationSKILL.md
# 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 remoteHit the Cloudflare REST API directly for operations that wrangler and MCP can't handle well. Bulk DNS, custom hostnames, email routing, cache purge, WAF rules, redirect rules, zone settings, Worker routes, D1 cross-database queries, R2 bulk operations, KV bulk read/write, Vectorize queries, Queues, and fleet-wide resource audits. Produces curl commands or scripts. Triggers: 'cloudflare api', 'bulk dns', 'custom hostname', 'email routing', 'cache purge', 'waf rule', 'd1 query', 'r2 bucket', 'kv bulk', 'vectorize query', 'audit resources', 'fleet operation'.
Scaffold and deploy Cloudflare Workers with Hono routing, Vite plugin, and Static Assets. Describe project, scaffold structure, configure bindings, deploy. Use whenever the user wants to create a Worker project, set up Hono on Cloudflare, configure D1 / R2 / KV / Queues bindings, or troubleshoot Worker export syntax, API route conflicts, HMR issues, or deployment failures.
Generate Drizzle ORM schemas for Cloudflare D1 databases with correct D1-specific patterns. Produces schema files, migration commands, type exports, and DATABASE_SCHEMA.md documentation. Handles D1 quirks: foreign keys always enforced, no native BOOLEAN/DATETIME types, 100 bound parameter limit, JSON stored as TEXT. Use when creating a new database, adding tables, or scaffolding a D1 data layer.
Generate database seed scripts with realistic sample data. Reads Drizzle schemas or SQL migrations, respects foreign key ordering, produces idempotent TypeScript or SQL seed files. Handles D1 batch limits, unique constraints, and domain-appropriate data. Use when populating dev/demo/test databases. Triggers: 'seed database', 'seed data', 'sample data', 'populate database', 'db seed', 'test data', 'demo data', 'generate fixtures'.
Scaffold Hono API routes for Cloudflare Workers. Produces route files, middleware, typed bindings, Zod validation, error handling, and API_ENDPOINTS.md documentation. Use after a project is set up with cloudflare-worker-builder or vite-flare-starter, when you need to add API routes, create endpoints, or generate API documentation.
Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per project.
Scaffold a full-stack Cloudflare app from the vite-flare-starter template — React 19 + Hono + D1+Drizzle + better-auth + Tailwind v4+shadcn/ui + TanStack Query + R2 + Workers AI. Run setup.sh to clone, configure, and deploy. Use whenever the user wants a batteries-included Cloudflare full-stack app, vite-flare-starter scaffold, or a React + Cloudflare app with auth + database + Workers AI ready to go.
Generate AI images using Gemini or GPT APIs directly. Covers model selection (Gemini for scenes; GPT Image 2 for text rendering, batch variations, multi-reference compositing; GPT Image 1.5 for transparent icons), the 5-part prompting framework, API calling patterns, multi-turn editing, and quality assurance. Produces photorealistic scenes, icons, illustrations, OG images, posters, infographics, and product shots. Use when building websites that need images, creating marketing assets, or generating visual content. Triggers: 'generate image', 'ai image', 'create hero image', 'make an icon', 'generate illustration', 'create og image', 'poster', 'infographic', 'image variations', 'gpt-image-2', 'ai art', 'image generation'.