Skip to main content

generate:migration

Use the built-in CLI command for generating migrations to easily get up and running. Psychic will automatically timestamp the filename for your migrations, keeping them in sequential order, and will generate boilerplate to get you started with writing a migration.

pnpm psy g:migration --help
pnpm psy g:migration add-deleted-at-to-posts deleted_at:datetime:optional

Usage

pnpm psy g:migration [options] <migrationName> [columnsWithTypes...]

Arguments

  • <migrationName>: End with -to-table-name or -from-table-name to prepopulate with an alterTable command.

  • [columnsWithTypes...]: Space separated snake-case (except for belongs_to model name) properties like this: title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal User:belongs_to

    All properties default to not nullable; null can be allowed by appending :optional: subtitle:string:optional

    Supported types

    • uuid, uuid[]: a column optimized for storing UUIDs
    • citext, citext[]: case insensitive text (indexes and queries are automatically case insensitive)
    • encrypted: encrypted text (used in conjunction with the @deco.Encrypted decorator)
    • string, string[]: varchar; allowed length defaults to 255, but may be customized, e.g.: subtitle:string:128 or subtitle:string:128:optional
    • text, text[]
    • boolean, boolean[]
    • date, date[]
    • datetime, datetime[]
    • time, time[]
    • timetz, timetz[]
    • integer, integer[]
    • decimal, decimal[]: precision,scale is required, e.g.: volume:decimal:3,2 or volume:decimal:3,2:optional
      • Leveraging arrays, add the "[]" suffix, e.g.: volume:decimal[]:3,2
    • jsonb: prefer this over json unless you specifically need Postgres's non-normalized json storage. Reach for either only when the data genuinely can't be modeled as columns or an associated table — see Column Types — JSON for when a jsonb column is (and isn't) the right call.
    • enum, enum[]: include the enum name to automatically create the enum: type:enum:room_types:bathroom,kitchen,bedroom or type:enum:room_types:bathroom,kitchen,bedroom:optional
      • Omit the enum values to leverage an existing enum (omits the enum type creation): type:enum:room_types or type:enum:room_types:optional
      • Leveraging arrays, add the "[]" suffix, e.g.: type:enum[]:room_types:bathroom,kitchen,bedroom
    • belongs_to: adds a foreign key to migration
      • Include the fully qualified model name, e.g., if the Coach model is in src/app/models/Health/Coach: Health/Coach:belongs_to
      • Aliased FK shorthand — append @alias to generate a foreign key with a custom column name. This is the canonical pattern for _by columns (e.g. canceled_by_id) and for multiple foreign keys to the same model: InternalUser@canceled_by:belongs_to:optional produces the canceled_by_id column and its index from one token. A standalone g:migration writes a migration and nothing else, so the canceledById property and the canceledBy association are hand-added to the model afterward; g:model and g:resource write them for you. Examples:
        • User@created_by:belongs_tocreated_by_id FK, createdBy association
        • Message@last_inbound:belongs_to:optionallast_inbound_id FK, lastInbound association
        • Message@last_outbound:belongs_to:optionallast_outbound_id FK, lastOutbound association

Options

  • --connection-name <connectionName>: The connection name you wish to use for your migration.
  • -h, --help: Display help for command.

Ending the migration name with -to-table-name will automatically include the underscored table name in the migration. When a name contains both markers, -to- wins regardless of where -from- appears; -from- only takes effect when the name has no -to- at all.

Because of this, a hand-written migration name (one with no columns passed, meant to be edited by hand) must avoid -to- and -from- entirely — either substring resolves the trailing text into a real table name and the generator fails when it can't find columns to pair with that table.

If you want to also generate a model for this migration, you can simply use the g:model or g:resource commands instead.

For schema changes that just add columns (or a foreign key) to an existing table, g:migration accepts the same column shorthand shown above — including Model:belongs_to and :optional. Hand-edit the generated migration only when the change isn't expressible as column shorthand, such as check constraints, enum alterations, or a custom backfill; see migrations for that reference.