Skip to main content
Glama
dj-pearson

Supabase Coolify MCP Server

by dj-pearson
rollback-example.md11.2 kB
# Migration Rollback Examples Complete examples of using migration rollback functionality. ## Example 1: Basic Migration with Rollback ### Create a safe migration ```typescript // Ask Claude: "Create a migration that adds a profiles table with full rollback support" // MCP Tool Call: create_migration_with_rollback({ name: "add_profiles_table", up_sql: ` CREATE TABLE IF NOT EXISTS public.profiles ( id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, username TEXT UNIQUE NOT NULL, full_name TEXT, avatar_url TEXT, bio TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_profiles_username ON public.profiles(username); ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; CREATE POLICY "Profiles are viewable by everyone" ON public.profiles FOR SELECT USING (true); CREATE POLICY "Users can update own profile" ON public.profiles FOR UPDATE USING (auth.uid() = id); `, down_sql: ` DROP POLICY IF EXISTS "Users can update own profile" ON public.profiles; DROP POLICY IF EXISTS "Profiles are viewable by everyone" ON public.profiles; DROP INDEX IF EXISTS idx_profiles_username; DROP TABLE IF EXISTS public.profiles CASCADE; ` }) ``` ### Result: ```json { "success": true, "data": { "version": "20231215143022", "name": "add_profiles_table", "status": "applied", "executed_at": "2023-12-15T14:30:22.123Z" }, "message": "Migration add_profiles_table deployed with rollback support" } ``` ### Test and rollback if needed ```typescript // If you encounter issues: "Rollback the profiles table migration" rollback_migration_with_down_sql({ version: "20231215143022" }) ``` ## Example 2: Complex Data Migration with Rollback ### Splitting a name field into first_name and last_name ```typescript create_migration_with_rollback({ name: "split_name_fields", up_sql: ` BEGIN; -- Add new columns ALTER TABLE public.users ADD COLUMN first_name TEXT, ADD COLUMN last_name TEXT; -- Migrate existing data UPDATE public.users SET first_name = SPLIT_PART(name, ' ', 1), last_name = CASE WHEN POSITION(' ' IN name) > 0 THEN SUBSTRING(name FROM POSITION(' ' IN name) + 1) ELSE '' END WHERE name IS NOT NULL; -- Make first_name required ALTER TABLE public.users ALTER COLUMN first_name SET NOT NULL; -- Remove old column ALTER TABLE public.users DROP COLUMN name; COMMIT; `, down_sql: ` BEGIN; -- Add back the old column ALTER TABLE public.users ADD COLUMN name TEXT; -- Migrate data back UPDATE public.users SET name = TRIM(CONCAT(first_name, ' ', COALESCE(last_name, ''))) WHERE first_name IS NOT NULL; -- Make name required ALTER TABLE public.users ALTER COLUMN name SET NOT NULL; -- Remove new columns ALTER TABLE public.users DROP COLUMN first_name, DROP COLUMN last_name; COMMIT; ` }) ``` ### If you need to rollback: ```typescript "The name split migration broke our user display, please rollback" rollback_migration_with_down_sql({ version: "20231215150000" }) ``` ## Example 3: Adding Indexes with Rollback ### Performance optimization that might need reverting ```typescript create_migration_with_rollback({ name: "add_performance_indexes", up_sql: ` -- Add indexes for common queries CREATE INDEX CONCURRENTLY idx_users_email_lower ON public.users(LOWER(email)); CREATE INDEX CONCURRENTLY idx_posts_user_created ON public.posts(user_id, created_at DESC); CREATE INDEX CONCURRENTLY idx_comments_post_created ON public.comments(post_id, created_at DESC); -- Add partial index for active users CREATE INDEX CONCURRENTLY idx_users_active ON public.users(last_active_at) WHERE deleted_at IS NULL; `, down_sql: ` -- Remove indexes DROP INDEX CONCURRENTLY IF EXISTS idx_comments_post_created; DROP INDEX CONCURRENTLY IF EXISTS idx_posts_user_created; DROP INDEX CONCURRENTLY IF EXISTS idx_users_email_lower; DROP INDEX CONCURRENTLY IF EXISTS idx_users_active; ` }) ``` ### If indexes cause write performance issues: ```typescript "The new indexes are slowing down writes, rollback immediately" rollback_migration_with_down_sql({ version: "20231215153000" }) ``` ## Example 4: Emergency Rollback ### Production issue - need immediate rollback ```typescript // Scenario: Migration broke production // Option 1: Rollback just the last migration "Emergency! Rollback the last migration NOW" rollback_last_migrations({ count: 1 }) // Result: { "success": true, "data": { "rolledBack": ["20231215160000"] }, "message": "Rolled back 1 migration(s)" } // Option 2: Rollback to a known good version "Rollback to version 20231215120000" rollback_to_version({ version: "20231215120000" }) // Result: { "success": true, "data": { "rolledBack": [ "20231215130000", "20231215140000", "20231215150000", "20231215160000" ] }, "message": "Rolled back 4 migration(s) to version 20231215120000" } ``` ## Example 5: Rollback with Data Preservation ### When you need to keep data during rollback ```typescript create_migration_with_rollback({ name: "restructure_user_settings", up_sql: ` BEGIN; -- Backup existing data CREATE TABLE user_settings_backup AS SELECT * FROM user_settings; -- Drop and recreate with new structure DROP TABLE user_settings; CREATE TABLE user_settings ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES auth.users(id), setting_key TEXT NOT NULL, setting_value JSONB, created_at TIMESTAMP DEFAULT NOW(), UNIQUE(user_id, setting_key) ); -- Migrate data from backup INSERT INTO user_settings (user_id, setting_key, setting_value) SELECT user_id, setting_name as setting_key, jsonb_build_object('value', setting_value) as setting_value FROM user_settings_backup; -- Drop backup table DROP TABLE user_settings_backup; COMMIT; `, down_sql: ` BEGIN; -- Backup new structure data CREATE TABLE user_settings_new_backup AS SELECT * FROM user_settings; -- Recreate old structure DROP TABLE user_settings; CREATE TABLE user_settings ( user_id UUID REFERENCES auth.users(id), setting_name TEXT, setting_value TEXT, PRIMARY KEY (user_id, setting_name) ); -- Restore data INSERT INTO user_settings (user_id, setting_name, setting_value) SELECT user_id, setting_key as setting_name, setting_value->>'value' as setting_value FROM user_settings_new_backup; -- Clean up DROP TABLE user_settings_new_backup; COMMIT; ` }) ``` ## Example 6: Testing Rollback Before Deployment ### Always test your rollback! ```typescript // 1. Create migration locally "Create a migration to add a comments table with rollback" create_migration_with_rollback({ name: "add_comments_table", up_sql: `CREATE TABLE comments (...);`, down_sql: `DROP TABLE comments CASCADE;` }) // 2. Test the migration "Check if comments table exists" execute_sql({ sql: "SELECT COUNT(*) FROM comments;" }) // 3. Test rollback "Test the rollback for the comments migration" rollback_migration_with_down_sql({ version: "..." }) // 4. Verify rollback worked "Check if comments table was removed" execute_sql({ sql: "SELECT COUNT(*) FROM comments;" }) // Should error: relation "comments" does not exist // 5. Re-apply for production "Re-apply the comments migration" create_migration_with_rollback({ name: "add_comments_table", up_sql: `CREATE TABLE comments (...);`, down_sql: `DROP TABLE comments CASCADE;` }) ``` ## Example 7: Multiple Migration Rollback ### Rolling back several related migrations ```typescript // You deployed 3 related migrations: // 1. add_user_profiles // 2. add_profile_images // 3. add_profile_badges // Something went wrong, need to rollback all 3 "Rollback the last 3 migrations" rollback_last_migrations({ count: 3 }) // Result: { "success": true, "data": { "rolledBack": [ "20231215143000", // add_profile_badges "20231215142000", // add_profile_images "20231215141000" // add_user_profiles ] }, "message": "Rolled back 3 migration(s)" } ``` ## Example 8: Partial Rollback with Fix ### Rollback and immediately fix ```typescript // 1. Rollback problematic migration "Rollback the add_email_verification migration" rollback_migration_with_down_sql({ version: "20231215140000" }) // 2. Create fixed version "Create a corrected email verification migration" create_migration_with_rollback({ name: "add_email_verification_fixed", up_sql: ` -- Corrected version with proper constraints ALTER TABLE public.users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE NOT NULL; ALTER TABLE public.users ADD COLUMN email_verified_at TIMESTAMP; -- Add constraint that if verified, must have timestamp ALTER TABLE public.users ADD CONSTRAINT check_email_verified CHECK ( (email_verified = FALSE AND email_verified_at IS NULL) OR (email_verified = TRUE AND email_verified_at IS NOT NULL) ); `, down_sql: ` ALTER TABLE public.users DROP CONSTRAINT IF EXISTS check_email_verified; ALTER TABLE public.users DROP COLUMN IF EXISTS email_verified_at; ALTER TABLE public.users DROP COLUMN IF EXISTS email_verified; ` }) ``` ## Best Practices Checklist When creating migrations with rollback: - [ ] Always include `IF EXISTS` / `IF NOT EXISTS` in SQL - [ ] Test both up and down SQL in development - [ ] Use transactions (BEGIN/COMMIT) for multi-step migrations - [ ] Backup data before risky migrations - [ ] Document what the down SQL does - [ ] Consider data preservation in rollbacks - [ ] Test rollback before deploying to production - [ ] Have a monitoring plan after deployment - [ ] Keep rollback SQL idempotent - [ ] Version control all migrations ## Common Rollback Patterns ### Pattern 1: Simple Table Creation ```sql -- Up CREATE TABLE IF NOT EXISTS users (...); -- Down DROP TABLE IF EXISTS users CASCADE; ``` ### Pattern 2: Adding Columns ```sql -- Up ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url TEXT; -- Down ALTER TABLE users DROP COLUMN IF EXISTS avatar_url; ``` ### Pattern 3: Data Migration ```sql -- Up CREATE TABLE new_table AS SELECT * FROM old_table; DROP TABLE old_table; -- Down CREATE TABLE old_table AS SELECT * FROM new_table; DROP TABLE new_table; ``` ### Pattern 4: Adding Constraints ```sql -- Up ALTER TABLE users ADD CONSTRAINT check_email CHECK (email LIKE '%@%'); -- Down ALTER TABLE users DROP CONSTRAINT IF EXISTS check_email; ``` --- **Remember**: Always test your rollbacks in a safe environment before relying on them in production!

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dj-pearson/supabase-coolify-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server