Create and manage database migrations safely. Use when adding tables, columns, indexes, RLS policies, or making any database schema change.
Use your migration tool to create the file:
# Drizzle (hand-written SQL)
# Create next sequential file in your migrations directory
# e.g., migrations/00XX_description.sql
# Prisma
npx prisma migrate dev --name description_here
# Supabase CLI
npx supabase migration new description_here
ALTER TABLE public.new_table ENABLE ROW LEVEL SECURITY;
IF NOT EXISTS, IF EXISTS)Example structure:
-- Migration: Add tags table with many-to-many relationship to jobs
-- Date: YYYY-MM-DD
CREATE TABLE IF NOT EXISTS public.tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT DEFAULT '#6B7280',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Enable RLS
ALTER TABLE public.tags ENABLE ROW LEVEL SECURITY;
-- RLS policies
CREATE POLICY "Users can manage own tags"
ON public.tags FOR ALL
USING (auth.uid() = user_id);
-- Indexes
CREATE INDEX idx_tags_user_id ON public.tags(user_id);
Update your TypeScript schema to match the migration exactly:
// Drizzle example
export const tags = pgTable('tags', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
color: text('color').default('#6B7280'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});
If your migration tool uses a journal or metadata file (e.g., Drizzle's _journal.json), update that too — the tool won't detect the migration without it.
If the migration affects shared types, enums, or validation schemas, update those too:
# Run typecheck to ensure schema types are correct
npm run typecheck # or pnpm typecheck
# Run linter to catch issues
npm run lint
# Supabase local
npx supabase db reset # Drops and recreates DB, runs all migrations
# Prisma
npx prisma migrate dev # Applies pending migrations to local DB
# Drizzle
npx drizzle-kit migrate # Runs migrations against DATABASE_URL
If no local database exists, review the SQL carefully before deploying. Consider setting up local development to test migrations safely.
# Supabase remote
npx supabase db push
# Prisma
npx prisma migrate deploy
# Drizzle (varies by setup — may run in build pipeline)
DATABASE_URL="..." npx drizzle-kit migrate
DROP TABLE or DROP COLUMN without explicit user confirmationALTER TABLE public.existing_table
ADD COLUMN IF NOT EXISTS new_column TEXT DEFAULT 'value';
-- Note: IF NOT EXISTS for enum values requires Postgres 9.3+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'new_status';
CREATE TABLE IF NOT EXISTS public.job_tags (
job_id UUID NOT NULL REFERENCES public.jobs(id) ON DELETE CASCADE,
tag_id UUID NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE,
PRIMARY KEY (job_id, tag_id)
);
ALTER TABLE public.job_tags ENABLE ROW LEVEL SECURITY;
CREATE INDEX idx_job_tags_job_id ON public.job_tags(job_id);
CREATE INDEX idx_job_tags_tag_id ON public.job_tags(tag_id);
CREATE INDEX IF NOT EXISTS idx_table_column ON public.table_name(column_name);
-- For partial indexes:
CREATE INDEX IF NOT EXISTS idx_active_users ON public.users(email) WHERE deleted_at IS NULL;
Update this skill with your project-specific details: