Guides creation, validation, and application of Supabase database migrations with RLS policy checks and type generation. Use when adding tables, modifying schema, or updating database structure.
Scanned 2/12/2026
Install via CLI
openskills install lifegenieai/claude-skills---
name: Managing Supabase Schema Migrations
description: Guides creation, validation, and application of Supabase database migrations with RLS policy checks and type generation. Use when adding tables, modifying schema, or updating database structure.
---
# Managing 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
```bash
# 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**
```sql
-- 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](../../modules/supabase-security.md)
### Step 3: Validate RLS Policies
**CRITICAL:** Every table MUST have RLS enabled and policies defined.
```bash
# 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 POLICY` statement
### Step 4: Test Migration Locally
Apply migration to local Supabase instance first:
```bash
# 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:
```bash
# 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:
```bash
# Find files using the affected table
grep -r "from('old_table_name')" app/ components/ lib/
```
Update TypeScript code to use new types:
```typescript
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:**
```bash
# 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
```sql
ALTER TABLE public.saved_recipes
ADD COLUMN difficulty VARCHAR(20) CHECK (difficulty IN ('easy', 'medium', 'hard'));
```
### Renaming a Column
```sql
ALTER TABLE public.saved_recipes
RENAME COLUMN old_name TO new_name;
```
### Adding an Index
```sql
CREATE INDEX idx_recipes_user_id ON public.saved_recipes(user_id);
```
### Adding a Foreign Key
```sql
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
```sql
CREATE POLICY "Users can view their own recipes"
ON public.saved_recipes
FOR SELECT
USING (user_id = auth.uid());
```
### Policy: Public read, authenticated write
```sql
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
```sql
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](../../modules/supabase-security.md#rls-policies)
## Rollback Strategy
If a migration causes issues in production:
**Option 1: Create reverse migration**
```bash
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
bash .claude/skills/supabase-migrations/scripts/create-migration.sh "migration_name"
```
Creates timestamped migration file.
### validate-rls.sh
```bash
bash .claude/skills/supabase-migrations/scripts/validate-rls.sh supabase/migrations/[file].sql
```
Checks for RLS policies in migration.
### update-types.sh
```bash
bash .claude/skills/supabase-migrations/scripts/update-types.sh
```
Regenerates TypeScript types from local database.
## Common Issues
### "Supabase not running"
```bash
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"
```bash
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:**
```bash
supabase migration new name
```
**Test locally:**
```bash
supabase db push
```
**Update types:**
```bash
supabase gen types typescript --local > lib/supabase/types.ts
```
**Apply to production:**
```bash
supabase db push --remote
```
## Related Documentation
- **Supabase security patterns:** [supabase-security.md](../../modules/supabase-security.md)
- **TypeScript type safety:** [typescript-standards.md](../../modules/typescript-standards.md)
- **Pre-commit checks:** Use `pre-commit-quality` skill after code changes
No comments yet. Be the first to comment!