Skip to content

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/:

  1. MIGRATION_DESCRIPTION_1_add_available_connection_slug_and_auth_type.md
    • Adds slug, display_name, auth_type to available_connections; backfills from name; drops name
    • Pre-req: 5 catalog rows with the canonical names
  2. MIGRATION_DESCRIPTION_2_add_connection_available_connection_id.md
    • Adds nullable available_connection_id to connections; backfills via JOIN using provider + config->>'capability'
    • Aborts if any row remains NULL — fix data and retry
  3. MIGRATION_DESCRIPTION_3_enforce_connection_available_connection_id_fk.md
    • Sets NOT NULL + FK ON DELETE RESTRICT + index
  4. MIGRATION_DESCRIPTION_4_drop_connection_provider_type_rename_name_to_label.md
    • Drops provider, type; renames namelabel (nullable)
    • Destructive: down() backfills are lossy; rollback past this migration is one-way for any row whose label was NULL

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.ts

If 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/available

All 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:

  • provider and type must be re-derived from slug
  • label can be NULL but the legacy name column 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.ts

Repeat the command to roll back additional steps.