Skip to main content
The CGIAR Risk Intelligence Tool uses Prisma for database schema management and migrations. The RDS PostgreSQL database is in a private VPC with no public internet access, so migrations must be run through the Worker Lambda function.

Why Remote Migrations?

The RDS instance is configured with:
  • Public Access: Disabled
  • Security Group: Allows inbound PostgreSQL (5432) only from Lambda security group
  • VPC: Private subnets (no internet gateway route)
Local machines cannot reach the database directly. The Worker Lambda is deployed inside the same VPC and can access the database.

Quick Start

From the project root:
This runs scripts/migrate-remote.sh, which sends Prisma migration SQL to the Worker Lambda’s run-sql action.

Migration Pipeline

The migrate-remote.sh script performs the following steps:

1. Discover Migration Files

Prisma migrations are stored in packages/api/prisma/migrations/ with timestamp-prefixed directories:
The script discovers all migration directories and sorts them by timestamp:

2. Send SQL to Worker Lambda

For each migration file, the script:
  1. Reads migration.sql
  2. Builds a JSON payload with the run-sql action
  3. Invokes the Worker Lambda
  4. Parses the response for success/failure
Worker Lambda Handler (packages/api/src/worker.ts):

3. Handle Already-Applied Migrations

If a migration was already applied (e.g., table already exists), Prisma throws an error:
The script detects this and skips the migration:

4. Update Migration Tracking Table

Prisma maintains a _prisma_migrations table to track which migrations have been applied:
The script inserts a row for each applied migration:
Why? This ensures prisma migrate deploy and prisma migrate status recognize the migrations as applied.

Creating New Migrations

Migrations are created during local development:

1. Modify Prisma Schema

Edit packages/api/prisma/schema.prisma:

2. Generate Migration

Run Prisma’s migration tool against your local PostgreSQL database:
Output:
Generated SQL (migration.sql):

3. Commit Migration

Commit the migration file to git:

4. Deploy to Remote Database

Run the remote migration script:
Output:

Deployment Order

When schema changes are deployed:
Critical: Migrations must run before deploying the API. If the API expects tables that don’t exist yet, it will crash.

Schema vs. Migrations vs. Seed

Local Development Workflow

Local development uses a separate PostgreSQL database (not the remote RDS):

1. Set Up Local Database

Create a local PostgreSQL database:

2. Configure .env

Create packages/api/.env:

3. Apply Migrations

Apply all migrations to the local database:

4. Seed Database

Populate the local database with sample data:
Seed Script (packages/api/prisma/seed.ts):

Troubleshooting

Migration fails with “relation already exists”

Cause: Migration was already applied (the tracking table is out of sync) Solution: The script automatically skips these. If the migration truly needs to run, drop the table manually via run-sql:
Then re-run pnpm migrate:remote.

DATABASE_URL not set

Cause: The CloudFormation template is missing the DATABASE_URL environment variable on the Worker Lambda Solution: Verify the CloudFormation template has:

Prisma Client out of sync with schema

Cause: Schema was modified but Prisma Client was not regenerated Solution: Regenerate Prisma Client:
This updates .prisma/client with the latest schema.

Migration timeout (Lambda times out after 120s)

Cause: Migration SQL is too large or slow Solution: Increase --cli-read-timeout in migrate-remote.sh:

Direct SQL Execution (Advanced)

For ad-hoc SQL queries or schema inspection, you can invoke the Worker Lambda directly:
Response:
Important: $executeRawUnsafe does not return query results, only the number of rows affected. Use Prisma Studio or a SQL client for inspecting data.

Prisma Studio (Local Only)

Prisma Studio provides a GUI for browsing and editing database data:
Accessible at: http://localhost:5555 Remote Database: Prisma Studio cannot connect to the remote RDS (private VPC). Use it only for local development.

Rollback Migrations

Prisma does not support automatic rollback. To rollback a migration:

1. Create a New Migration

Write the inverse SQL manually:
Edit migration.sql:

2. Apply the Rollback Migration

Better approach: Modify the schema and generate a new forward migration instead of rolling back.

Best Practices

  • Never edit migration.sql manually — Always use prisma migrate dev to generate migrations
  • Test migrations locally first — Run against local PostgreSQL before deploying to RDS
  • One migration per feature — Keep migrations small and focused
  • Commit migrations to git — Migrations are part of the codebase, not infrastructure
  • Run migrations before deploying API — Ensure schema is up-to-date before code expects new tables
  • Avoid destructive migrations — Drop tables only when absolutely necessary (backup first)

Migration Safety

  • RDS Backups: 7-day retention (automated)
  • Snapshot on Deletion: RDS stack has DeletionPolicy: Snapshot (production)
  • Test Environment: Always test migrations in dev/staging before production
  • Idempotent Migrations: Use IF NOT EXISTS, IF EXISTS where possible
  • Transaction Wrapping: Each migration runs in a transaction (automatic rollback on failure)

Schema Management Tools