Appearance
Runbook: Connections Redesign Migration
Breaking refactor: replaces the legacy connections.connections.provider + type + name columns with available_connection_id + label and introduces slug + display_name + auth_type on the catalog. Applied as 4 TypeORM migrations in strict order, plus a post-deploy catalog seed call.
Reference change: #286 under milestone connections-architecture-cleanup.
Pre-checks (before starting)
Run a read-only verification to confirm DB state matches the assumptions baked into the migrations:
sql
-- Expect exactly 5 rows with the legacy slug values
SELECT name, COUNT(*) FROM connections.available_connections
GROUP BY name;
-- Expected output:
-- name | count
-- ---------+-------
-- calendar| 1
-- gmail | 1
-- sheets | 1
-- smtp | 1
-- telegram| 1
-- Confirm every user connection can be mapped. The critical case is google_workspace:
-- every such row MUST carry config->>'capability' ∈ {sheets,calendar}
SELECT provider, config->>'capability' AS capability, COUNT(*)
FROM connections.connections
GROUP BY provider, capability;If any google_workspace row has capability = NULL or an unexpected value, fix the row manually BEFORE running migration 2 — it will abort otherwise.
Run order
Migrations must run in strict numerical order. The file names live under apps/api/src/modules/connections/infrastructure/migrations/:
MIGRATION_DESCRIPTION_1_add_available_connection_slug_and_auth_type.md- Adds
slug,display_name,auth_typetoavailable_connections; backfills fromname; dropsname - Pre-req: 5 catalog rows with the canonical names
- Adds
MIGRATION_DESCRIPTION_2_add_connection_available_connection_id.md- Adds nullable
available_connection_idtoconnections; backfills via JOIN usingprovider+config->>'capability' - Aborts if any row remains NULL — fix data and retry
- Adds nullable
MIGRATION_DESCRIPTION_3_enforce_connection_available_connection_id_fk.md- Sets NOT NULL + FK
ON DELETE RESTRICT+ index
- Sets NOT NULL + FK
MIGRATION_DESCRIPTION_4_drop_connection_provider_type_rename_name_to_label.md- Drops
provider,type; renamesname→label(nullable) - Destructive:
down()backfills are lossy; rollback past this migration is one-way for any row whoselabelwas NULL
- Drops
Each MD carries its full Up/Down SQL, pre-checks, and rollback notes. Read them before running.
Execute
From the repo root, with a DB backup already in place:
bash
# Preview the pending migration list
pnpm --filter api typeorm migration:show -- -d src/shared/infrastructure/db/data-source.ts
# Apply them all (the 4 migrations run in order based on their timestamp)
pnpm --filter api typeorm migration:run -- -d src/shared/infrastructure/db/data-source.tsIf any migration fails, the transaction rolls back that step. Inspect the log, correct the data, and re-run.
Post-migration: seed the catalog metadata
The migrations preserve only the slug and auth_type values; display_name, icon, cover, and description come from the in-code registry. Call the seed endpoint once the API is deployed:
http
POST /connections/available/seed
Authorization: Bearer <platform-admin-jwt>This endpoint is @CheckPolicies('manage.platform.connections.available-connection') — only platform admins with that uuid7 policy can invoke it. The handler performs an UPSERT by slug, so subsequent calls are idempotent and only refresh mutable display fields (never slug or auth_type).
Verify via:
http
GET /connections/availableAll 5 entries should now carry a non-null displayName that matches the registry (Gmail, Google Sheets, Google Calendar, SMTP, Telegram).
Rollback
Rollback past migrations 1-3 is safe and preserves data. Rollback past migration 4 is lossy because:
providerandtypemust be re-derived fromsluglabelcan be NULL but the legacynamecolumn was NOT NULL — any NULL label is backfilled with an empty string (visible to users)
To roll back:
bash
# One step back (rolls back migration 4 only)
pnpm --filter api typeorm migration:revert -- -d src/shared/infrastructure/db/data-source.tsRepeat the command to roll back additional steps.
Related
- Available Connections Catalog — catalog entity + CRUD endpoints
- Auth Discovery Flow — how the panel branches on
authType - Registry Contract — how the in-code registry stays aligned with the DB catalog
- GitHub EPIC: #285 Connections architecture cleanup