Drizzle ORM Database Migrations (TypeScript) Migration-first database development workflow using Drizzle ORM for TypeScript/JavaScript projects. When to Use This Skill Use this skill when: - Working with Drizzle ORM in TypeScript/JavaScript projects - Need to create or modify database schema - Want migration-first development workflow - Setting up new database tables or columns - Need to ensure schema consistency across environments Core Principle: Migration-First Development Critical Rule : Schema changes ALWAYS start with migrations, never code-first. Why Migration-First? - ✅ SQL migrations…

);\n```\n\n## Configuration\n\n### drizzle.config.ts\n\n```typescript\nimport type { Config } from 'drizzle-kit';\n\nexport default {\n schema: './src/lib/db/schema/index.ts',\n out: './drizzle',\n driver: 'pg',\n dbCredentials: {\n connectionString: process.env.DATABASE_URL!,\n },\n} satisfies Config;\n```\n\n### package.json Scripts\n\n```json\n{\n \"scripts\": {\n \"db:generate\": \"drizzle-kit generate:pg\",\n \"db:push\": \"drizzle-kit push:pg\",\n \"db:studio\": \"drizzle-kit studio\",\n \"db:check\": \"drizzle-kit check:pg\",\n \"db:up\": \"drizzle-kit up:pg\"\n }\n}\n```\n\n## Migration Testing Workflow\n\n### Local Testing\n\n```bash\n# 1. Create migration\necho \"CREATE TABLE test (...)\" > drizzle/0009_test.sql\n\n# 2. Generate TypeScript\npnpm db:generate\n\n# 3. Push to local database\npnpm db:push\n\n# 4. Verify schema\npnpm db:check\n\n# 5. Test in application\npnpm dev\n# Manually test affected features\n\n# 6. Run tests\npnpm test\n```\n\n### Rollback Strategy\n\n```sql\n-- drizzle/0010_add_feature.sql (up migration)\nCREATE TABLE new_feature (...);\n\n-- drizzle/0010_add_feature_down.sql (down migration)\nDROP TABLE new_feature;\n```\n\nApply rollback:\n```bash\n# Manually run down migration\npsql $DATABASE_URL -f drizzle/0010_add_feature_down.sql\n```\n\n## Best Practices\n\n### Do's\n- ✅ Write SQL migrations first\n- ✅ Use descriptive migration names\n- ✅ Add indexes for foreign keys\n- ✅ Include constraints in migrations\n- ✅ Test migrations on staging before production\n- ✅ Commit snapshots to version control\n- ✅ Organize schemas by domain\n- ✅ Use `drizzle-kit check` in CI\n\n### Don'ts\n- ❌ Never write TypeScript schema before SQL migration\n- ❌ Don't skip staging testing\n- ❌ Don't modify old migrations (create new ones)\n- ❌ Don't forget to add indexes\n- ❌ Don't use `drizzle-kit push` in production (use proper migrations)\n- ❌ Don't commit generated files without snapshots\n\n## Troubleshooting\n\n### Schema Drift Detected\n**Error**: `Schema drift detected`\n\n**Solution**:\n```bash\n# Check what changed\npnpm drizzle-kit check\n\n# Regenerate snapshots\npnpm drizzle-kit generate\n\n# Review changes and commit\ngit add drizzle/meta/\ngit commit -m \"Update schema snapshots\"\n```\n\n### Migration Fails on Staging\n**Error**: Migration fails with data constraint violation\n\n**Solution**:\n1. Rollback migration\n2. Create data migration script\n3. Run data migration first\n4. Then run schema migration\n\n```sql\n-- First: Migrate data\nUPDATE users SET status = 'active' WHERE status IS NULL;\n\n-- Then: Add constraint\nALTER TABLE users\nALTER COLUMN status SET NOT NULL;\n```\n\n### TypeScript Types Out of Sync\n**Error**: TypeScript types don't match database\n\n**Solution**:\n```bash\n# Regenerate everything\npnpm db:generate\npnpm tsc --noEmit\n\n# If still broken, check schema files\n# Ensure column names match SQL exactly\n```\n\n## Related Skills\n\n- `universal-data-database-migration` - Universal migration patterns\n- `toolchains-typescript-data-drizzle` - Drizzle ORM usage patterns\n- `toolchains-typescript-core` - TypeScript best practices\n- `universal-debugging-verification-before-completion` - Verification workflows","attachment_filenames":["metadata.json"],"attachments":[{"filename":"metadata.json","content":"{\n \"name\": \"drizzle-migrations\",\n \"version\": \"1.0.0\",\n \"category\": \"toolchain\",\n \"toolchain\": \"typescript\",\n \"framework\": \"drizzle\",\n \"tags\": [\n \"drizzle\",\n \"migrations\",\n \"database\",\n \"sql\",\n \"typescript\",\n \"schema\",\n \"orm\"\n ],\n \"entry_point_tokens\": 250,\n \"full_tokens\": 3024,\n \"author\": \"claude-mpm-skills\",\n \"license\": \"MIT\",\n \"requires\": [\n \"toolchains-typescript-core\",\n \"toolchains-typescript-data-drizzle\"\n ],\n \"updated\": \"2025-12-08\",\n \"source_path\": \"toolchains/typescript/data/drizzle-migrations/SKILL.md\",\n \"repository\": \"https://github.com/bobmatnyc/claude-mpm-skills\"\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":627,"content_sha256":"ceed76eca17103c658aee4899b30fb673586596969f5122484b9a2b1b72b0a89"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Drizzle ORM Database Migrations (TypeScript)","type":"text"}]},{"type":"paragraph","content":[{"text":"Migration-first database development workflow using Drizzle ORM for TypeScript/JavaScript projects.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Working with Drizzle ORM in TypeScript/JavaScript projects","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Need to create or modify database schema","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Want migration-first development workflow","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up new database tables or columns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Need to ensure schema consistency across environments","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principle: Migration-First Development","type":"text"}]},{"type":"paragraph","content":[{"text":"Critical Rule","type":"text","marks":[{"type":"strong"}]},{"text":": Schema changes ALWAYS start with migrations, never code-first.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Why Migration-First?","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ SQL migrations are the single source of truth","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Prevents schema drift between environments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Enables rollback and versioning","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Forces explicit schema design decisions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ TypeScript types generated from migrations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ CI/CD can validate schema changes","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Anti-Pattern (Code-First)","type":"text"}]},{"type":"paragraph","content":[{"text":"❌ ","type":"text"},{"text":"WRONG","type":"text","marks":[{"type":"strong"}]},{"text":": Writing TypeScript schema first","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// DON'T DO THIS FIRST\nexport const users = pgTable('users', {\n id: uuid('id').primaryKey(),\n email: text('email').notNull(),\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Correct Pattern (Migration-First)","type":"text"}]},{"type":"paragraph","content":[{"text":"✅ ","type":"text"},{"text":"CORRECT","type":"text","marks":[{"type":"strong"}]},{"text":": Write SQL migration first","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0001_add_users_table.sql\nCREATE TABLE users (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n email TEXT NOT NULL UNIQUE,\n created_at TIMESTAMP DEFAULT NOW()\n);","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Complete Migration Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Design Schema in SQL Migration","type":"text"}]},{"type":"paragraph","content":[{"text":"Create descriptive SQL migration file:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0001_create_school_calendars.sql\nCREATE TABLE school_calendars (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,\n start_date DATE NOT NULL,\n end_date DATE NOT NULL,\n academic_year TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n);\n\n-- Add indexes for query performance\nCREATE INDEX idx_school_calendars_school_id ON school_calendars(school_id);\nCREATE INDEX idx_school_calendars_academic_year ON school_calendars(academic_year);\n\n-- Add constraints\nALTER TABLE school_calendars\n ADD CONSTRAINT check_date_range\n CHECK (end_date > start_date);","type":"text"}]},{"type":"paragraph","content":[{"text":"Naming Convention","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use sequential numbers: ","type":"text"},{"text":"0001_","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"0002_","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Descriptive names: ","type":"text"},{"text":"create_school_calendars","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"add_user_roles","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Format: ","type":"text"},{"text":"XXXX_descriptive_name.sql","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Generate TypeScript Definitions","type":"text"}]},{"type":"paragraph","content":[{"text":"Drizzle Kit generates TypeScript types from SQL:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Generate TypeScript schema and snapshots\npnpm drizzle-kit generate\n\n# Or using npm\nnpm run db:generate","type":"text"}]},{"type":"paragraph","content":[{"text":"What This Creates","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TypeScript schema files (if using ","type":"text"},{"text":"drizzle-kit push","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Snapshot files in ","type":"text"},{"text":"drizzle/meta/XXXX_snapshot.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Migration metadata","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Create Schema Snapshot","type":"text"}]},{"type":"paragraph","content":[{"text":"Snapshots enable schema drift detection:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"// drizzle/meta/0001_snapshot.json (auto-generated)\n{\n \"version\": \"5\",\n \"dialect\": \"postgresql\",\n \"tables\": {\n \"school_calendars\": {\n \"name\": \"school_calendars\",\n \"columns\": {\n \"id\": {\n \"name\": \"id\",\n \"type\": \"uuid\",\n \"primaryKey\": true,\n \"notNull\": true,\n \"default\": \"gen_random_uuid()\"\n },\n \"school_id\": {\n \"name\": \"school_id\",\n \"type\": \"uuid\",\n \"notNull\": true\n }\n }\n }\n }\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Snapshots in Version Control","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Commit snapshots to git","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Enables drift detection in CI","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Documents schema history","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Implement TypeScript Schema","type":"text"}]},{"type":"paragraph","content":[{"text":"Now write TypeScript schema that mirrors SQL migration:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// src/lib/db/schema/school/calendar.ts\nimport { pgTable, uuid, date, text, timestamp } from 'drizzle-orm/pg-core';\nimport { schools } from './school';\n\nexport const schoolCalendars = pgTable('school_calendars', {\n id: uuid('id').primaryKey().defaultRandom(),\n schoolId: uuid('school_id')\n .notNull()\n .references(() => schools.id, { onDelete: 'cascade' }),\n startDate: date('start_date').notNull(),\n endDate: date('end_date').notNull(),\n academicYear: text('academic_year').notNull(),\n createdAt: timestamp('created_at').defaultNow(),\n updatedAt: timestamp('updated_at').defaultNow(),\n});\n\n// Type inference\nexport type SchoolCalendar = typeof schoolCalendars.$inferSelect;\nexport type NewSchoolCalendar = typeof schoolCalendars.$inferInsert;","type":"text"}]},{"type":"paragraph","content":[{"text":"Key Points","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Column names match SQL exactly: ","type":"text"},{"text":"school_id","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"'school_id'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TypeScript property names use camelCase: ","type":"text"},{"text":"schoolId","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Constraints and indexes defined in SQL, not TypeScript","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Foreign keys reference other tables","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Organize Schemas by Domain","type":"text"}]},{"type":"paragraph","content":[{"text":"Structure schemas for maintainability:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"src/lib/db/schema/\n├── index.ts # Export all schemas\n├── school/\n│ ├── index.ts\n│ ├── district.ts\n│ ├── holiday.ts\n│ ├── school.ts\n│ └── calendar.ts\n├── providers.ts\n├── cart.ts\n└── users.ts","type":"text"}]},{"type":"paragraph","content":[{"text":"index.ts","type":"text","marks":[{"type":"strong"}]},{"text":" (export all):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// src/lib/db/schema/index.ts\nexport * from './school';\nexport * from './providers';\nexport * from './cart';\nexport * from './users';","type":"text"}]},{"type":"paragraph","content":[{"text":"school/index.ts","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// src/lib/db/schema/school/index.ts\nexport * from './district';\nexport * from './holiday';\nexport * from './school';\nexport * from './calendar';","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Add Quality Check to CI","type":"text"}]},{"type":"paragraph","content":[{"text":"Validate schema consistency in CI/CD:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/quality.yml\nname: Quality Checks\n\non:\n pull_request:\n branches: [main, develop]\n push:\n branches: [main]\n\njobs:\n quality:\n runs-on: ubuntu-latest\n\n steps:\n - uses: actions/checkout@v4\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: '20'\n cache: 'pnpm'\n\n - name: Install dependencies\n run: pnpm install --frozen-lockfile\n\n - name: Check database schema drift\n run: pnpm drizzle-kit check\n\n - name: Verify migrations (dry-run)\n run: pnpm drizzle-kit push --dry-run\n env:\n DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}\n\n - name: Run type checking\n run: pnpm tsc --noEmit\n\n - name: Lint code\n run: pnpm lint","type":"text"}]},{"type":"paragraph","content":[{"text":"CI Checks Explained","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"drizzle-kit check","type":"text","marks":[{"type":"code_inline"}]},{"text":": Validates snapshots match schema","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"drizzle-kit push --dry-run","type":"text","marks":[{"type":"code_inline"}]},{"text":": Tests migration without applying","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type checking: Ensures TypeScript compiles","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Linting: Enforces code style","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 7: Test on Staging","type":"text"}]},{"type":"paragraph","content":[{"text":"Before production, test migration on staging:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1. Run migration on staging\nSTAGING_DATABASE_URL=\"...\" pnpm drizzle-kit push\n\n# 2. Verify schema\npnpm drizzle-kit check\n\n# 3. Test affected API routes\ncurl https://staging.example.com/api/schools/calendars\n\n# 4. Check for data integrity issues\n# Run queries to verify data looks correct\n\n# 5. Monitor logs for errors\n# Check application logs for migration-related errors","type":"text"}]},{"type":"paragraph","content":[{"text":"Staging Checklist","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Migration runs without errors","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Schema drift check passes","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"API routes using new schema work correctly","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No data integrity issues","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Application logs show no errors","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Query performance acceptable","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Migration Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Adding a Column","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0005_add_user_phone.sql\nALTER TABLE users\nADD COLUMN phone TEXT;\n\n-- Add index if querying by phone\nCREATE INDEX idx_users_phone ON users(phone);","type":"text"}]},{"type":"paragraph","content":[{"text":"TypeScript:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"export const users = pgTable('users', {\n id: uuid('id').primaryKey(),\n email: text('email').notNull(),\n phone: text('phone'), // New column\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Creating a Junction Table","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0006_create_provider_specialties.sql\nCREATE TABLE provider_specialties (\n provider_id UUID NOT NULL REFERENCES providers(id) ON DELETE CASCADE,\n specialty_id UUID NOT NULL REFERENCES specialties(id) ON DELETE CASCADE,\n PRIMARY KEY (provider_id, specialty_id)\n);\n\nCREATE INDEX idx_provider_specialties_provider ON provider_specialties(provider_id);\nCREATE INDEX idx_provider_specialties_specialty ON provider_specialties(specialty_id);","type":"text"}]},{"type":"paragraph","content":[{"text":"TypeScript:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"export const providerSpecialties = pgTable('provider_specialties', {\n providerId: uuid('provider_id')\n .notNull()\n .references(() => providers.id, { onDelete: 'cascade' }),\n specialtyId: uuid('specialty_id')\n .notNull()\n .references(() => specialties.id, { onDelete: 'cascade' }),\n}, (table) => ({\n pk: primaryKey(table.providerId, table.specialtyId),\n}));","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Modifying Column Type","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0007_change_price_to_decimal.sql\nALTER TABLE services\nALTER COLUMN price TYPE DECIMAL(10, 2);","type":"text"}]},{"type":"paragraph","content":[{"text":"TypeScript:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { decimal } from 'drizzle-orm/pg-core';\n\nexport const services = pgTable('services', {\n id: uuid('id').primaryKey(),\n name: text('name').notNull(),\n price: decimal('price', { precision: 10, scale: 2 }).notNull(),\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Adding Constraints","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0008_add_email_constraint.sql\nALTER TABLE users\nADD CONSTRAINT users_email_unique UNIQUE (email);\n\nALTER TABLE users\nADD CONSTRAINT users_email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}

Drizzle ORM Database Migrations (TypeScript) Migration-first database development workflow using Drizzle ORM for TypeScript/JavaScript projects. When to Use This Skill Use this skill when: - Working with Drizzle ORM in TypeScript/JavaScript projects - Need to create or modify database schema - Want migration-first development workflow - Setting up new database tables or columns - Need to ensure schema consistency across environments Core Principle: Migration-First Development Critical Rule : Schema changes ALWAYS start with migrations, never code-first. Why Migration-First? - ✅ SQL migrations…

);","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Configuration","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"drizzle.config.ts","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import type { Config } from 'drizzle-kit';\n\nexport default {\n schema: './src/lib/db/schema/index.ts',\n out: './drizzle',\n driver: 'pg',\n dbCredentials: {\n connectionString: process.env.DATABASE_URL!,\n },\n} satisfies Config;","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"package.json Scripts","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"scripts\": {\n \"db:generate\": \"drizzle-kit generate:pg\",\n \"db:push\": \"drizzle-kit push:pg\",\n \"db:studio\": \"drizzle-kit studio\",\n \"db:check\": \"drizzle-kit check:pg\",\n \"db:up\": \"drizzle-kit up:pg\"\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Migration Testing Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Local Testing","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1. Create migration\necho \"CREATE TABLE test (...)\" > drizzle/0009_test.sql\n\n# 2. Generate TypeScript\npnpm db:generate\n\n# 3. Push to local database\npnpm db:push\n\n# 4. Verify schema\npnpm db:check\n\n# 5. Test in application\npnpm dev\n# Manually test affected features\n\n# 6. Run tests\npnpm test","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Rollback Strategy","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- drizzle/0010_add_feature.sql (up migration)\nCREATE TABLE new_feature (...);\n\n-- drizzle/0010_add_feature_down.sql (down migration)\nDROP TABLE new_feature;","type":"text"}]},{"type":"paragraph","content":[{"text":"Apply rollback:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Manually run down migration\npsql $DATABASE_URL -f drizzle/0010_add_feature_down.sql","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Do's","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Write SQL migrations first","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use descriptive migration names","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Add indexes for foreign keys","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Include constraints in migrations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Test migrations on staging before production","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Commit snapshots to version control","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Organize schemas by domain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅ Use ","type":"text"},{"text":"drizzle-kit check","type":"text","marks":[{"type":"code_inline"}]},{"text":" in CI","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Don'ts","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Never write TypeScript schema before SQL migration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Don't skip staging testing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Don't modify old migrations (create new ones)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Don't forget to add indexes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Don't use ","type":"text"},{"text":"drizzle-kit push","type":"text","marks":[{"type":"code_inline"}]},{"text":" in production (use proper migrations)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌ Don't commit generated files without snapshots","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Schema Drift Detected","type":"text"}]},{"type":"paragraph","content":[{"text":"Error","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"Schema drift detected","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Solution","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check what changed\npnpm drizzle-kit check\n\n# Regenerate snapshots\npnpm drizzle-kit generate\n\n# Review changes and commit\ngit add drizzle/meta/\ngit commit -m \"Update schema snapshots\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Migration Fails on Staging","type":"text"}]},{"type":"paragraph","content":[{"text":"Error","type":"text","marks":[{"type":"strong"}]},{"text":": Migration fails with data constraint violation","type":"text"}]},{"type":"paragraph","content":[{"text":"Solution","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rollback migration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create data migration script","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run data migration first","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Then run schema migration","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"sql"},"content":[{"text":"-- First: Migrate data\nUPDATE users SET status = 'active' WHERE status IS NULL;\n\n-- Then: Add constraint\nALTER TABLE users\nALTER COLUMN status SET NOT NULL;","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"TypeScript Types Out of Sync","type":"text"}]},{"type":"paragraph","content":[{"text":"Error","type":"text","marks":[{"type":"strong"}]},{"text":": TypeScript types don't match database","type":"text"}]},{"type":"paragraph","content":[{"text":"Solution","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Regenerate everything\npnpm db:generate\npnpm tsc --noEmit\n\n# If still broken, check schema files\n# Ensure column names match SQL exactly","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Related Skills","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"universal-data-database-migration","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Universal migration patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"toolchains-typescript-data-drizzle","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Drizzle ORM usage patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"toolchains-typescript-core","type":"text","marks":[{"type":"code_inline"}]},{"text":" - TypeScript best practices","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"universal-debugging-verification-before-completion","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Verification workflows","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"drizzle-migrations","tags":[],"author":"@skillopedia","source":{"stars":78,"repo_name":"claude-mpm-skills","origin_url":"https://github.com/bobmatnyc/claude-mpm-skills/blob/HEAD/toolchains/typescript/data/drizzle-migrations/SKILL.md","repo_owner":"bobmatnyc","body_sha256":"8621b5b993b079bb156ff091a79dc79d1698cc8c9592633f3f4761e5bee70040","cluster_key":"05eade871b9387f472fc2cb54297a8b2b46f57677a267e9ec8aebabe17978cd3","clean_bundle":{"format":"clean-skill-bundle-v1","source":"bobmatnyc/claude-mpm-skills/toolchains/typescript/data/drizzle-migrations/SKILL.md","text_bytes":627,"source_commit":"718070a7d622921b01687799a1f9613f36c6f615","skills_sh_rank":10854,"attachment_count":1,"text_attachments":1,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"toolchains/typescript/data/drizzle-migrations/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"1.0.0","category":"data-analytics","category_label":"Data"},"exact_dupes_collapsed_into_this":0},"version":"1.0.0","category":"data-analytics","import_tag":"skills-sh-top20k-2026-11","description":"Migration-first database development workflow using Drizzle ORM for TypeScript/J...","user-invocable":false,"progressive_disclosure":{"entry_point":{"summary":"Migration-first database development workflow using Drizzle ORM for TypeScript/J...","quick_start":"1. Review the core concepts below. 2. Apply patterns to your use case. 3. Follow best practices for implementation.","when_to_use":"When working with drizzle-migrations or related functionality."}},"disable-model-invocation":true}},"renderedAt":1793686551118}

Drizzle ORM Database Migrations (TypeScript) Migration-first database development workflow using Drizzle ORM for TypeScript/JavaScript projects. When to Use This Skill Use this skill when: - Working with Drizzle ORM in TypeScript/JavaScript projects - Need to create or modify database schema - Want migration-first development workflow - Setting up new database tables or columns - Need to ensure schema consistency across environments Core Principle: Migration-First Development Critical Rule : Schema changes ALWAYS start with migrations, never code-first. Why Migration-First? - ✅ SQL migrations…