Managing Supabase Schema Migrations
npx machina-cli add skill Hildegaardchiasmal966/claude-skills/supabase-migrations --openclawManaging Supabase Schema Migrations
Safe workflow for database schema changes with automatic type generation and RLS validation.
When to Use This Skill
- Adding new tables
- Modifying existing columns
- Creating indexes
- Adding or updating RLS policies
- Any database schema changes
Migration Workflow
Follow these steps for safe migrations:
Step 1: Create Migration File
# Create a new migration with descriptive name
supabase migration new add_recipe_tags_table
# Or with bash helper
bash .claude/skills/supabase-migrations/scripts/create-migration.sh "add_recipe_tags_table"
This creates: supabase/migrations/[timestamp]_add_recipe_tags_table.sql
Step 2: Write Migration SQL
Edit the generated file with your schema changes.
Example: Adding a new table
-- Create recipe_tags table
CREATE TABLE IF NOT EXISTS public.recipe_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
recipe_id UUID NOT NULL REFERENCES public.saved_recipes(id) ON DELETE CASCADE,
tag VARCHAR(50) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
-- Ensure unique tag per recipe
UNIQUE(recipe_id, tag)
);
-- Create index for faster lookups
CREATE INDEX idx_recipe_tags_recipe_id ON public.recipe_tags(recipe_id);
CREATE INDEX idx_recipe_tags_tag ON public.recipe_tags(tag);
-- Enable RLS
ALTER TABLE public.recipe_tags ENABLE ROW LEVEL SECURITY;
-- RLS Policy: Users can only see tags for recipes they own
CREATE POLICY "Users can view their own recipe tags"
ON public.recipe_tags
FOR SELECT
USING (
recipe_id IN (
SELECT id FROM public.saved_recipes WHERE user_id = auth.uid()
)
);
-- RLS Policy: Users can insert tags for their own recipes
CREATE POLICY "Users can insert tags for their recipes"
ON public.recipe_tags
FOR INSERT
WITH CHECK (
recipe_id IN (
SELECT id FROM public.saved_recipes WHERE user_id = auth.uid()
)
);
-- RLS Policy: Users can delete tags from their own recipes
CREATE POLICY "Users can delete their recipe tags"
ON public.recipe_tags
FOR DELETE
USING (
recipe_id IN (
SELECT id FROM public.saved_recipes WHERE user_id = auth.uid()
)
);
Common migration patterns: See supabase-security.md
Step 3: Validate RLS Policies
CRITICAL: Every table MUST have RLS enabled and policies defined.
# Check if migration includes RLS
bash .claude/skills/supabase-migrations/scripts/validate-rls.sh supabase/migrations/[your-migration-file].sql
Checks for:
ALTER TABLE ... ENABLE ROW LEVEL SECURITY- At least one
CREATE POLICYstatement
Step 4: Test Migration Locally
Apply migration to local Supabase instance first:
# Ensure local Supabase is running
supabase start
# Apply migration to local database
supabase db push
# Check status
supabase db diff
If errors occur:
- Fix the SQL in migration file
- Reset local DB:
supabase db reset - Try again:
supabase db push
Step 5: Regenerate TypeScript Types
After successful local migration, update types:
# Generate types from local database
bash .claude/skills/supabase-migrations/scripts/update-types.sh
# Or manually:
supabase gen types typescript --local > lib/supabase/types.ts
This updates lib/supabase/types.ts with new schema.
Step 6: Update Application Code
Search codebase for files that need updating:
# Find files using the affected table
grep -r "from('old_table_name')" app/ components/ lib/
Update TypeScript code to use new types:
import type { Database } from '@/lib/supabase/types'
type RecipeTag = Database['public']['Tables']['recipe_tags']['Row']
Step 7: Test Changes
- Run build:
npm run build(must pass) - Run tests:
npm test(must pass) - Test affected features manually
Step 8: Apply to Production
Only after local testing passes:
# Push migration to remote database
supabase db push --remote
# Or via Supabase Dashboard:
# 1. Copy migration SQL
# 2. Run in SQL Editor
# 3. Verify with table view
Important: Migrations are irreversible in production. Always test locally first.
Migration Safety Checklist
Before applying to production:
- Migration tested locally with
supabase db push? - RLS enabled on all new tables?
- RLS policies created (SELECT, INSERT, UPDATE, DELETE)?
- Indexes created for foreign keys and frequent queries?
- Types regenerated with
update-types.sh? - Application code updated to use new schema?
- Build passes (
npm run build)? - Tests pass (
npm test)? - Manual testing of affected features completed?
Common Migration Patterns
Adding a Column
ALTER TABLE public.saved_recipes
ADD COLUMN difficulty VARCHAR(20) CHECK (difficulty IN ('easy', 'medium', 'hard'));
Renaming a Column
ALTER TABLE public.saved_recipes
RENAME COLUMN old_name TO new_name;
Adding an Index
CREATE INDEX idx_recipes_user_id ON public.saved_recipes(user_id);
Adding a Foreign Key
ALTER TABLE public.recipe_images
ADD CONSTRAINT fk_recipe_images_recipe_id
FOREIGN KEY (recipe_id)
REFERENCES public.saved_recipes(id)
ON DELETE CASCADE;
RLS Policy Patterns
Policy: User owns resource
CREATE POLICY "Users can view their own recipes"
ON public.saved_recipes
FOR SELECT
USING (user_id = auth.uid());
Policy: Public read, authenticated write
CREATE POLICY "Anyone can view recipes"
ON public.saved_recipes
FOR SELECT
USING (true);
CREATE POLICY "Authenticated users can insert"
ON public.saved_recipes
FOR INSERT
WITH CHECK (auth.uid() IS NOT NULL);
Policy: Relationship-based access
CREATE POLICY "Users can view tags for their recipes"
ON public.recipe_tags
FOR SELECT
USING (
recipe_id IN (
SELECT id FROM public.saved_recipes WHERE user_id = auth.uid()
)
);
More RLS patterns: See supabase-security.md
Rollback Strategy
If a migration causes issues in production:
Option 1: Create reverse migration
supabase migration new revert_add_recipe_tags
# Write SQL to undo changes
# - DROP TABLE
# - DROP COLUMN
# - etc.
Option 2: Restore from backup (via Supabase Dashboard)
- Settings → Database → Point-in-time Recovery
- Select time before migration
- Restore (creates new project)
Prevention is better: Always test locally first!
Script Usage
create-migration.sh
bash .claude/skills/supabase-migrations/scripts/create-migration.sh "migration_name"
Creates timestamped migration file.
validate-rls.sh
bash .claude/skills/supabase-migrations/scripts/validate-rls.sh supabase/migrations/[file].sql
Checks for RLS policies in migration.
update-types.sh
bash .claude/skills/supabase-migrations/scripts/update-types.sh
Regenerates TypeScript types from local database.
Common Issues
"Supabase not running"
supabase start
"Migration file not found"
Check path: supabase/migrations/[timestamp]_name.sql
"RLS validation failed"
Add ALTER TABLE ... ENABLE ROW LEVEL SECURITY and CREATE POLICY statements.
"Types not updating"
supabase db reset
supabase db push
bash .claude/skills/supabase-migrations/scripts/update-types.sh
"Build errors after migration"
- Check type imports:
Database['public']['Tables']['table_name']['Row'] - Update queries to match new schema
- Fix any breaking column renames
Quick Reference
Create migration:
supabase migration new name
Test locally:
supabase db push
Update types:
supabase gen types typescript --local > lib/supabase/types.ts
Apply to production:
supabase db push --remote
Related Documentation
- Supabase security patterns: supabase-security.md
- TypeScript type safety: typescript-standards.md
- Pre-commit checks: Use
pre-commit-qualityskill after code changes
Source
git clone https://github.com/Hildegaardchiasmal966/claude-skills/blob/master/supabase-migrations/SKILL.mdView on GitHub Overview
This skill guides you through creating, validating, and applying Supabase migrations with automatic type generation and RLS checks. Use it when adding tables, modifying schema, or updating database structure to keep changes safe and consistent.
How This Skill Works
You create a migration file, write the SQL changes (including enabling RLS and policies), validate that RLS is present, test the migration locally, regenerate TypeScript types, and then update your application code to use the new schema.
When to Use It
- Adding new tables
- Modifying existing columns
- Creating indexes
- Adding or updating RLS policies
- Any database schema changes
Quick Start
- Step 1: Create migration file with a descriptive name using supabase migration new or the helper script
- Step 2: Write your SQL changes, including table creation, indexes, and RLS policies
- Step 3: Validate RLS, test locally, regenerate types, and update application code
Best Practices
- Name migrations descriptively so you can trace changes over time
- Always enable RLS and define policies for new/updated tables
- Validate RLS with the provided script to ensure coverage
- Test migrations locally before pushing to production
- Regenerate TypeScript types after successful migrations and update app code
Example Use Cases
- Adding a new table recipe_tags with RLS policies and indexes (example in the migration content)
- Enabling RLS on a table and creating policies for SELECT, INSERT, and DELETE
- Creating indexes to improve lookup performance for a newly added table
- Regenerating TypeScript types to reflect the new schema
- Updating application code to consume new or modified table types