d1-create-migration
Guided migration creation for Cloudflare D1 with schema change validation, SQL generation, and testing workflow. Use when user wants to create migration, modify schema, add tables/indexes, or change database structure.
- 0
- Installs
- —
- Rating
- —
- Success rate
- 1
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 2df4d74b70472000… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
d1-create-migration.md
D1 Create Migration Command
Overview
Guided migration creation with validation, SQL generation, and safe testing workflow.
Prerequisites
- Existing D1 database configured in wrangler.jsonc
- migrations/ directory exists (created during setup)
- wrangler CLI available
Steps
Step 1: Migration Type
Use AskUserQuestion to determine what kind of migration to create.
Prompt: "What type of migration are you creating?"
Options:
- "Add new table"
- "Modify existing table (add/remove columns)"
- "Add indexes"
- "Data migration (update existing records)"
- "Custom SQL"
Store as: migrationType
Step 2: Gather Migration Details
Based on migrationType, collect specific details:
If "Add new table":
Ask:
- Table name:
tableName - Columns (comma-separated):
columnsList- Example: "id INTEGER PRIMARY KEY, name TEXT, email TEXT UNIQUE"
- Add foreign keys? (yes/no):
hasForeignKeys- If yes, ask: "Which table.column?" →
foreignKeyReferences
- If yes, ask: "Which table.column?" →
If "Modify existing table":
Ask:
- Table name:
tableName - Change type:
- "Add column"
- "Drop column"
- "Rename column"
- Store as:
changeType
- Column details:
columnDetails
If "Add indexes":
Ask:
- Table name:
tableName - Columns to index (comma-separated):
indexColumns - Index type:
- "Single column index"
- "Composite index (multiple columns)"
- "Unique index"
- Store as:
indexType
If "Data migration":
Ask:
- Description of data change:
description - Warning: "Data migrations should be idempotent and tested in staging first. Proceed?"
If "Custom SQL":
Ask:
- "Provide SQL file path or paste SQL directly"
- Store as:
customSQL
Step 3: Generate Migration File
Create migration file with timestamp:
# Generate timestamp
TIMESTAMP=$(date +%Y%m%d%H%M%S)
# Create migration filename
MIGRATION_FILE="migrations/${TIMESTAMP}_<description>.sql"
Generate SQL based on migration type:
Add Table Example:
-- Migration: Add <tableName> table
-- Created: <timestamp>
CREATE TABLE IF NOT EXISTS <tableName> (
id INTEGER PRIMARY KEY AUTOINCREMENT,
<columnsList>,
created_at INTEGER DEFAULT (unixepoch())
);
-- Indexes for foreign keys and commonly queried columns
<for each indexed column>
CREATE INDEX IF NOT EXISTS idx_<tableName>_<column> ON <tableName>(<column>);
</for each>
-- Optimize query planner
PRAGMA optimize;
Add Column Example:
-- Migration: Add column <columnName> to <tableName>
-- Created: <timestamp>
-- SQLite doesn't support ADD COLUMN IF NOT EXISTS
-- Check if column exists first (defensive)
ALTER TABLE <tableName> ADD COLUMN <columnName> <type> <constraints>;
PRAGMA optimize;
Add Index Example:
-- Migration: Add index on <tableName>(<columns>)
-- Created: <timestamp>
CREATE INDEX IF NOT EXISTS idx_<tableName>_<columns> ON <tableName>(<columns>);
PRAGMA optimize;
Data Migration Example:
-- Migration: <description>
-- Created: <timestamp>
-- WARNING: Test in staging first!
-- Example: Update all users' status
UPDATE users
SET status = 'active'
WHERE status IS NULL;
PRAGMA optimize;
Write Migration File: Use Write tool to create the SQL file in migrations/
Step 4: Validation
Run automated checks on generated migration:
1. Syntax Validation (dry-run):
# Note: Dry-run not directly supported, but can test locally
wrangler d1 execute <database-name> --local --file=<migration-file>
2. Best Practices Check:
- ✓ Uses
IF NOT EXISTSfor CREATE statements? - ✓ Has
PRAGMA optimizeat end? - ✓ Includes indexes for foreign keys?
- ✓ Uses INTEGER for timestamps?
- ⚠ Has ALTER TABLE without IF NOT EXISTS? (warn user)
- ⚠ Has DROP operations? (destructive, warn user)
3. Warn if Destructive:
⚠️ Warning: This migration contains destructive operations:
- DROP TABLE <table>
- DROP COLUMN <column>
Recommendations:
1. Backup data first (Time Travel: wrangler d1 time-travel...)
2. Test in staging environment
3. Ensure data is no longer needed
4. Consider archiving instead of dropping
Output Example:
✓ Migration validation passed
- Uses IF NOT EXISTS
- Includes PRAGMA optimize
- Indexes created for foreign keys
⚠️ Recommendation: Test in local database first
Step 5: Test in Local Database (Optional)
Ask User: "Test migration in local development database first? (Recommended)"
If yes:
# Apply to local D1
wrangler d1 migrations apply <database-name> --local
# Verify schema
wrangler d1 execute <database-name> --local --command ".schema <tableName>"
# Test query
wrangler d1 execute <database-name> --local --command "SELECT * FROM <tableName>"
Show Results:
✓ Local migration applied successfully
Schema after migration:
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
status TEXT DEFAULT 'pending', ← New column
created_at INTEGER DEFAULT (unixepoch())
);
Indexes:
- idx_users_email
- idx_users_status ← New index
Error Handling:
❌ Local migration failed:
Error: UNIQUE constraint failed: users.email
Possible causes:
1. Duplicate emails exist in data
2. Column constraint conflicts with existing data
Solution:
1. Edit migration file to handle duplicates
2. Clean data first, then reapply migration
Step 6: Apply to Remote (Production)
Warning Prompt:
⚠️ Apply migration to remote (production) database?
This will modify your production database:
- Database: <database-name>
- Migration: <migration-file>
Ensure you have:
✓ Tested locally
✓ Reviewed SQL carefully
✓ Time Travel backup available (retention: 7-30 days)
Proceed?
If user confirms:
wrangler d1 migrations apply <database-name> --remote
Monitor Application:
✓ Migration applied successfully
⚠️ Important: Monitor your application for 10-15 minutes
- Check error logs: wrangler tail <worker-name>
- Watch metrics: wrangler d1 insights <database-name>
- Verify queries still work as expected
Rollback available via Time Travel (if needed):
wrangler d1 time-travel restore <database-name> --timestamp=<before-migration>
Step 7: Verification
Verify migration was applied successfully:
# 1. List applied migrations
wrangler d1 migrations list <database-name>
# 2. Verify schema changes
wrangler d1 execute <database-name> --command ".schema <tableName>"
# 3. Test query with new structure
wrangler d1 execute <database-name> --command "SELECT * FROM <tableName> LIMIT 1"
Output Example:
✓ Migration Verification Complete
Applied Migrations:
- 0001_initial_schema.sql (2025-01-10)
- 0002_add_status_column.sql (2025-01-15) ← New
Schema:
- Table: users (6 columns)
- Indexes: 2
- Foreign Keys: 0
Test Query:
[
{
"id": 1,
"email": "user@example.com",
"name": "Test User",
"status": "pending",
"created_at": 1705334400
}
]
Step 8: Next Steps
Success Message:
✅ Migration Applied Successfully!
Migration Details:
- File: <migration-file>
- Database: <database-name>
- Type: <migrationType>
- Applied: Local ✓ | Remote ✓
Next Steps:
1. Update your code to use new schema:
```typescript
// Example: Query with new column
const { results } = await env.DB.prepare(
'SELECT * FROM users WHERE status = ?'
).bind('active').all();
-
Monitor performance impact:
wrangler d1 insights <database-name> -
If new indexes added, check query efficiency:
wrangler d1 execute <database-name> --command \ "EXPLAIN QUERY PLAN SELECT * FROM <tableName> WHERE <indexed-column> = ?"
Rollback Strategy:
- Time Travel restore available for days
- Restore command:
wrangler d1 time-travel restore
--timestamp= - Backup timestamp:
📚 References:
- Migration best practices: Load
references/best-practices.md - Query optimization: Load
references/query-patterns.md
---
## Safety Features
### Destructive Operations Warning
**Before executing DROP operations**:
🚨 DESTRUCTIVE OPERATION DETECTED
This migration will permanently delete data:
- DROP TABLE (deletes all rows)
- DROP COLUMN (deletes column data)
⚠️ Actions Required:
-
Backup data via Time Travel: Current retention: <7 or 30> days Restore point:
-
Export data if needed: wrangler d1 execute --command "SELECT * FROM " > backup.json
-
Confirm understanding of data loss
Proceed only if certain data is no longer needed.
### Data Migration Safeguards
**For UPDATE/DELETE operations**:
⚠️ Data Migration Detected
Best Practices:
-
Test with LIMIT first: UPDATE users SET status = 'active' WHERE status IS NULL LIMIT 10;
-
Use transactions (batch): -- All succeed or all fail env.DB.batch([...updates])
-
Make it idempotent: -- Safe to run multiple times UPDATE users SET status = 'active' WHERE status IS NULL AND status != 'active';
-
Check affected rows: SELECT COUNT(*) FROM users WHERE status IS NULL; -- Verify count matches expectations before UPDATE
---
## Common Migration Patterns
### Add Column with Default
```sql
-- Migration: Add status column with default value
ALTER TABLE users ADD COLUMN status TEXT DEFAULT 'pending';
-- Create index if column will be filtered
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
PRAGMA optimize;
Add Foreign Key (via new table)
-- Note: SQLite doesn't support ADD FOREIGN KEY after table creation
-- Must recreate table or create new table with FK
-- Migration: Add orders table with foreign key
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
total REAL NOT NULL,
created_at INTEGER DEFAULT (unixepoch()),
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id);
PRAGMA optimize;
Rename Column (SQLite 3.25+)
-- Migration: Rename column email_address to email
ALTER TABLE users RENAME COLUMN email_address TO email;
PRAGMA optimize;
Backfill Data
-- Migration: Backfill timestamps for existing records
UPDATE users
SET created_at = unixepoch()
WHERE created_at IS NULL;
PRAGMA optimize;
Error Handling
Migration File Already Exists
❌ Error: Migration file already exists with same timestamp
Solution:
Wait 1 second and retry (timestamps are second-precision)
Or manually specify migration name
Syntax Error in SQL
❌ Error: SQL syntax error at line 5
Solution:
1. Review generated SQL in migrations/<file>
2. Fix syntax error
3. Test locally: wrangler d1 migrations apply <db> --local
4. Reapply to remote
Constraint Violation
❌ Error: UNIQUE constraint failed: users.email
Cause: Adding UNIQUE constraint on column with duplicate values
Solution:
1. Find duplicates:
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;
2. Clean duplicates first (separate migration)
3. Then apply UNIQUE constraint
Summary
This command provides guided migration creation through 8 steps:
- Determine migration type (via AskUserQuestion)
- Gather migration details
- Generate migration SQL file
- Validate syntax and best practices
- Test in local database (optional)
- Apply to remote/production
- Verify schema changes
- Provide next steps and rollback info
Output: Safe, tested migration with validation checks, rollback strategy, and next steps.
When to Use: Any schema change - add table/column/index, modify structure, or data migration.
Files
1- d1-create-migration.md
d7ab7ab80111.9 KB
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from secondsky/claude-skills8
Add a better-auth plugin to an existing project. Configures server and client plugins with proper imports.
Interactive setup wizard for better-auth authentication. Guides through database, framework, OAuth providers, and plugin configuration.
Run a focused blindspot pass for unfamiliar, ambiguous, or high-risk work
Debug Bun applications and diagnose common issues
Deploy Bun applications to various platforms
Initialize a new Bun project with optional framework selection
Migrate existing Node.js/npm projects to Bun
Optimize Bun application performance and bundle size