Overview
The TWIGZ backend uses the@convex-dev/migrations library to define and run database migrations. Migrations handle schema evolution by backfilling new fields on existing records, ensuring all documents conform to the latest schema requirements.
Location: convex/migrations.ts
Migrations process records one at a time using
migrateOne. Each migration only modifies records that are missing the target fields, making them safe to run multiple times (idempotent).Running Migrations
Migrations are executed using the exportedrun function, which is a Convex mutation that can be invoked from the Convex dashboard or programmatically.
Migration Functions
addStatusToStores
Adds astatus field to existing store documents that are missing it.
| Property | Value |
|---|---|
| Table | stores |
| Field Added | status |
| Default Value | "pending" |
| Condition | Only applies to stores without a status field |
ensureStoreFields
A comprehensive migration that ensures all stores have the required fields. Handles multiple missing fields in a single pass.| Property | Value |
|---|---|
| Table | stores |
| Fields Added | status, phoneNumberVerified |
| Default Values | status: "pending", phoneNumberVerified: false |
| Condition | Only updates stores missing one or more of the target fields |
addUsernamesToCustomers
Generates unique usernames for existing customers that do not have one. Derives the username from the customer’s name, appending numeric suffixes to resolve conflicts.| Property | Value |
|---|---|
| Table | customers |
| Field Added | username |
| Default Value | Derived from customer.name (lowercased, alphanumeric, max 12 chars) |
| Condition | Only applies to customers without a username field or with a falsy value |
Base Name
Takes the customer’s name, converts to lowercase, strips non-alphanumeric characters, and truncates to 12 characters.
Uniqueness Check
Queries the
by_username index to verify availability. If taken, appends an incrementing numeric suffix (e.g., john1, john2).addPrivacyToCustomers
Sets a default privacy setting for existing customers that do not have one.| Property | Value |
|---|---|
| Table | customers |
| Field Added | isPrivate |
| Default Value | true |
| Condition | Only applies to customers without isPrivate or where it is undefined |
Migration Summary
| Migration | Table | Fields | Default Values |
|---|---|---|---|
addStatusToStores | stores | status | "pending" |
ensureStoreFields | stores | status, phoneNumberVerified | "pending", false |
addUsernamesToCustomers | customers | username | Derived from name |
addPrivacyToCustomers | customers | isPrivate | true |
Best Practices
When to Create a New Migration
When to Create a New Migration
Create a new migration whenever you add a required field to the schema that existing documents will not have. This ensures all existing records are backfilled with appropriate default values.
Idempotency
Idempotency
All migrations check whether the target field already exists before modifying a record. This means migrations can safely be re-run without causing duplicate updates or data corruption.
Running Order
Running Order
The
run function from @convex-dev/migrations handles execution order automatically. Migrations that have already been applied are skipped on subsequent runs.