The Supabase Annotations package now includes comprehensive migration support to handle existing database schemas gracefully. Instead of generating CREATE TABLE statements that fail when tables already exist, you can now configure the generator to produce migration-safe SQL.
- Behavior: Original behavior - generates
CREATE TABLEstatements - Use Case: Initial database setup or when you're certain tables don't exist
- Output: Standard
CREATE TABLE table_name (...);
# build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'createOnly'- Behavior: Generates
CREATE TABLE IF NOT EXISTSstatements - Use Case: Safe table creation that won't fail if tables already exist
- Output:
CREATE TABLE IF NOT EXISTS table_name (...);
# build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'createIfNotExists'- Behavior: Creates table if not exists, then adds missing columns
- Use Case: Schema evolution - handles both new tables and field additions
- Output:
CREATE TABLE IF NOT EXISTS table_name (...);- Followed by conditional
ALTER TABLE ADD COLUMNstatements
# build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'createOrAlter'
enable_column_adding: true
generate_do_blocks: true- Behavior: Only generates
ALTER TABLEstatements to add missing columns - Use Case: When you only want to add new fields to existing tables
- Output: Only conditional
ALTER TABLE ADD COLUMNstatements
# build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'alterOnly'
enable_column_adding: true- Behavior: Drops existing table and creates fresh one (destructive)
- Use Case: Development environments where data loss is acceptable
- Output:
DROP TABLE IF EXISTS table_name CASCADE;- Followed by
CREATE TABLE table_name (...);
# build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'dropAndRecreate'targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
# Migration strategy
migration_mode: 'createOrAlter' # Required
# Column management
enable_column_adding: true # Add missing columns
enable_column_modification: true # Modify existing columns
enable_column_dropping: false # Drop unused columns (dangerous)
# Additional features
enable_index_creation: true # Create missing indexes
enable_constraint_modification: true # Modify constraints
generate_do_blocks: true # Use PostgreSQL DO blocks for conditionals
# Standard options
format_sql: true
generate_comments: trueWhen generate_do_blocks: true (recommended), the generator produces PostgreSQL DO blocks for safer conditional operations:
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'age'
) THEN
ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0;
END IF;
END $$;When generate_do_blocks: false, it uses simpler statements (PostgreSQL 9.6+):
ALTER TABLE users ADD COLUMN IF NOT EXISTS age INTEGER NOT NULL DEFAULT 0;- Initial Setup: Use
createOnlyorcreateIfNotExists - Schema Evolution: Use
createOrAlterwhen adding new fields - Field-Only Updates: Use
alterOnlyfor incremental updates - Reset Development DB: Use
dropAndRecreatewhen needed
For production environments, use createOrAlter or alterOnly to safely handle schema changes:
# production-build.yaml
targets:
$default:
builders:
supabase_annotations|schema_builder:
options:
migration_mode: 'createOrAlter'
enable_column_adding: true
enable_column_modification: false # Be conservative in production
enable_column_dropping: false # Never drop columns in production
generate_do_blocks: true # Use safe conditional logicGiven this Dart class:
@DatabaseTable(name: 'users')
class User {
@DatabaseColumn(type: ColumnType.uuid, isPrimaryKey: true)
String? id;
@DatabaseColumn(type: ColumnType.text, isUnique: true)
String email = '';
@DatabaseColumn(type: ColumnType.integer, defaultValue: DefaultValue.number(0))
int age = 0; // New field added later
}CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
age INTEGER NOT NULL DEFAULT 0
);CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
age INTEGER NOT NULL DEFAULT 0
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'age'
) THEN
ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0;
END IF;
END $$;DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'age'
) THEN
ALTER TABLE users ADD COLUMN age INTEGER NOT NULL DEFAULT 0;
END IF;
END $$;- Always backup before running migration scripts in production
- Test migrations in a development environment first
- Use transactions when executing multiple migration statements
- Monitor performance as ALTER TABLE can be slow on large tables
- Consider column order - new columns are added at the end
- Validate constraints before enabling them on existing data
- Column already exists errors: Use
createOrAlteroralterOnlymode - Permission errors: Ensure database user has ALTER TABLE privileges
- Large table performance: Consider adding columns with NULL defaults first
- Data type conflicts: Test type changes carefully in development
- DO Blocks: PostgreSQL 9.0+
- IF NOT EXISTS: PostgreSQL 9.1+ for CREATE TABLE, 9.6+ for ALTER TABLE
- ADD COLUMN IF NOT EXISTS: PostgreSQL 9.6+
Choose generate_do_blocks: true for maximum compatibility.