PostgreSQL expert for .sql migration files, CREATE TABLE, ALTER TABLE, indexes, constraints, foreign keys, schema changes, docker/postgres/migrations/, init.sql, idempotent SQL, transactions,...
Expert in PostgreSQL schema management and migrations following project conventions.
Use this skill when:
ALL database changes MUST have a migration file.
Without a migration, changes will NOT deploy to production.
Create Migration File
# In docker/postgres/migrations/
# Name: NNN_description.sql (e.g., 003_add_user_roles.sql)
Write Idempotent Migration
BEGIN;
-- Create table only if it doesn't exist
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
"createdAt" TIMESTAMP DEFAULT NOW()
);
-- Add column only if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'role'
) THEN
ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user';
END IF;
END $$;
-- Record migration
INSERT INTO schema_migrations (version, description)
VALUES ('003', 'Add user roles')
ON CONFLICT (version) DO NOTHING;
COMMIT;
Update init.sql
# Add the same schema to docker/postgres/init.sql
# For fresh database installations
Test Migration Locally
# Apply migration to test database
docker exec -i st44-db-test psql -U postgres -d st44_test < docker/postgres/migrations/003_add_user_roles.sql
# Verify it worked
docker exec -it st44-db-test psql -U postgres -d st44_test -c "\d users"
Test Idempotency
# Run migration again - should not error
docker exec -i st44-db-test psql -U postgres -d st44_test < docker/postgres/migrations/003_add_user_roles.sql
ALL columns MUST use camelCase with double quotes.
CREATE TABLE users (
id UUID PRIMARY KEY,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"createdAt" TIMESTAMP DEFAULT NOW()
);
CREATE TABLE users (
id UUID PRIMARY KEY,
first_name TEXT NOT NULL, -- NO snake_case!
last_name TEXT NOT NULL, -- NO snake_case!
);
Why: Consistency across entire stack (TypeScript, API, database).
docker/postgres/migrations/
āāā 001_initial_schema.sql
āāā 002_add_households.sql
āāā 003_add_user_roles.sql
āāā 004_add_tasks_table.sql
NNN_description.sqlBEGIN;
-- Your schema changes here
-- Use IF NOT EXISTS for idempotency
-- Always record the migration
INSERT INTO schema_migrations (version, description)
VALUES ('NNN', 'Description of changes')
ON CONFLICT (version) DO NOTHING;
COMMIT;
CREATE TABLE IF NOT EXISTS table_name (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"columnName" TEXT NOT NULL
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'role'
) THEN
ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user';
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'users_email_unique'
) THEN
ALTER TABLE users ADD CONSTRAINT users_email_unique UNIQUE (email);
END IF;
END $$;
Before marking migration complete:
docker/postgres/migrations/NNN_description.sql\d table_name in psqlCREATE TABLE table_name (
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);
"createdAt" TIMESTAMP DEFAULT NOW(),
"updatedAt" TIMESTAMP DEFAULT NOW()
"userId" UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
role TEXT CHECK (role IN ('admin', 'parent', 'child'))
npm run db:test:up
docker exec -i st44-db-test psql -U postgres -d st44_test < docker/postgres/migrations/003_add_user_roles.sql
docker exec -it st44-db-test psql -U postgres -d st44_test -c "\d users"
# Run same migration again - should not error
docker exec -i st44-db-test psql -U postgres -d st44_test < docker/postgres/migrations/003_add_user_roles.sql
npm run db:test:down
.claude/agents/agent-database.mdFor detailed patterns and examples:
.claude/agents/agent-database.md - Complete agent specificationdocker/postgres/init.sql - Current database schemadocker/postgres/migrations/ - Existing migration examplesCLAUDE.md - Project-wide conventionsIf you create a migration file and it passes testing: ā It WILL deploy to production ā It WILL run automatically ā Schema changes are guaranteed
If you DON'T create a migration file: ā Changes will NOT deploy ā Production will be out of sync ā Backend will fail with schema errors
This is why migration-first is mandatory.
Before marking work complete: