Skip to content

Command Line Tools

wheels generate migration

Generate database migration files using templates.

Terminal window
wheels generate migration name=<migrationName> [options]
# Can also be used as:
wheels g migration name=<migrationName> [options]
  • Named parameters: param=value (e.g., name=CreateUsersTable, table=users)
  • Flag parameters: --flag equals flag=true (e.g., --force equals force=true)
  • Param with value: --param=value equals param=value (e.g., --description="User management")

Recommended: Use named parameters: wheels generate migration name=CreateUsersTable --create=users

The wheels generate migration command creates database migration files using pre-built templates. Migrations provide version control for your database schema, allowing you to incrementally modify your database structure and roll back changes when needed. The command intelligently detects migration type from the name pattern and uses appropriate templates with transaction handling and error management.

ArgumentDescriptionDefault
nameMigration name (e.g., CreateUsersTable, AddEmailToUsers)Required
  • Must start with a letter
  • Can contain letters, numbers, and underscores
  • Use PascalCase for readability
  • Should describe the migration action clearly

Common Patterns:

  • Create[Table]Table - Creates a new table
  • Add[Column]To[Table] - Adds column(s) to existing table
  • Remove[Column]From[Table] - Removes column(s) from table
  • Rename[OldColumn]To[NewColumn] - Renames a column
  • Change[Column]In[Table] - Modifies column type/properties
  • CreateIndexOn[Table] - Adds an index
  • RemoveIndexFrom[Table] - Removes an index
OptionDescriptionValid ValuesDefault
createTable name to create (forces create_table type)Valid table name""
tableTable name to modify (for column operations)Valid table name""
dropTable name to drop (forces remove_table type)Valid table name""
descriptionMigration description (added as hint)Any descriptive text""
forceOverwrite existing migration filetrue, falsefalse

The command automatically detects the migration type and uses the appropriate template:

TypeTemplateDetected PatternExample
Create Tablecreate-table.txtCreate*TableCreateUsersTable
Remove Tableremove-table.txtDrop*Table, Remove*TableDropUsersTable
Add Columncreate-column.txtAdd*To*AddEmailToUsers
Remove Columnremove-column.txtRemove*From*RemoveAgeFromUsers
Change Columnchange-column.txtChange*In*ChangeNameInUsers
Rename Columnrename-column.txtRename*To*RenameFirstnameToFullname
Add Indexcreate-index.txtCreateIndexOn*CreateIndexOnUsersEmail
Remove Indexremove-index.txtRemoveIndexFrom*RemoveIndexFromUsers
Blankblank.txtAny other patternCustomMigration
Terminal window
wheels generate migration name=CreateUsersTable

Creates migration using create-table.txt template with transaction handling.

Terminal window
wheels generate migration name=CreateUsersTable --create=users

Forces create_table type and uses “users” as table name.

Terminal window
wheels generate migration name=AddEmailToUsers --table=users

Creates migration using create-column.txt template.

Terminal window
wheels generate migration name=RemoveAgeFromUsers --table=users

Creates migration using remove-column.txt template.

Terminal window
wheels generate migration name=DropProductsTable

Creates migration using remove-table.txt template.

Terminal window
wheels generate migration name=CreateUsersTable --description="User authentication table"

Adds description as hint to the migration component.

Terminal window
wheels generate migration name=MigrateUserData --description="Move user data to new structure"

Creates blank migration for custom migration code.

Terminal window
wheels generate migration name=CreateUsersTable --force=true

Overwrites existing migration file.

component extends="wheels.migrator.Migration" hint="CreateUsersTable" {
function up() {
transaction {
try {
t = createTable(name = 'users', force='false', id='true', primaryKey='id');
t.timestamps();
t.create();
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
dropTable('users');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}
component extends="wheels.migrator.Migration" hint="AddEmailToUsers" {
function up() {
transaction {
try {
addColumn(table='users', columnType='string', columnName='column_name', default='', null='true', limit='255');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
removeColumn(table='users', columnName='column_name');
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}
component extends="wheels.migrator.Migration" hint="MigrateUserData" {
function up() {
transaction {
try {
// your code goes here
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
function down() {
transaction {
try {
// your code goes here
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
}

The command infers table names from migration name patterns:

Migration NameDetected TypeInferred TableTemplate Used
CreateUsersTablecreate_tableuserscreate-table.txt
DropProductsTableremove_tableproductsremove-table.txt
AddEmailToUserscreate_columnuserscreate-column.txt
RemoveAgeFromUsersremove_columnusersremove-column.txt
RenameFirstnameToFullnamerename_column(needs —table)rename-column.txt
ChangeStatusInOrderschange_columnorderschange-column.txt
CreateIndexOnUsersEmailcreate_indexuserscreate-index.txt
CustomDataMigrationblankN/Ablank.txt

All migrations use templates from /cli/src/templates/dbmigrate/:

  • blank.txt - Empty migration with transaction wrapper
  • create-table.txt - Create table with timestamps
  • remove-table.txt - Drop table with reversible down()
  • create-column.txt - Add column with options
  • remove-column.txt - Remove column
  • change-column.txt - Modify column type/properties
  • rename-column.txt - Rename column
  • create-index.txt - Add index to table
  • remove-index.txt - Remove index from table

You can override default templates by placing custom versions in /app/snippets/dbmigrate/:

Terminal window
# Copy template to customize
cp cli/src/templates/dbmigrate/create-table.txt app/snippets/dbmigrate/create-table.txt
# Edit your custom template
# Wheels will automatically use your version

Templates use placeholder variables that are replaced during generation:

VariableDescriptionExample
|DBMigrateDescription|Migration hint/descriptionCreateUsersTable
|tableName|Table nameusers
|columnName|Column nameemail
|columnType|Column typestring
|force|Force table creationfalse
|id|Auto-create ID columntrue
|primaryKey|Primary key nameid
|allowNull|Allow NULL valuestrue
|limit|Column length limit255
|default|Default value""
|unique|Unique indexfalse
Terminal window
wheels generate migration name=CreateUsersTable
/app/migrator/migrations/20250119123045_CreateUsersTable.cfc

Add columns to the migration:

function up() {
transaction {
try {
t = createTable(name = 'users', force='false', id='true', primaryKey='id');
t.string(columnNames='firstName,lastName', allowNull=false);
t.string(columnNames='email', allowNull=false, limit=100);
t.boolean(columnNames='active', default=true);
t.timestamps();
t.create();
// Add index on email
addIndex(table='users', columnNames='email', unique=true);
} catch (any e) {
local.exception = e;
}
if (StructKeyExists(local, "exception")) {
transaction action="rollback";
Throw(errorCode = "1", detail = local.exception.detail, message = local.exception.message, type = "any");
} else {
transaction action="commit";
}
}
}
Terminal window
wheels dbmigrate info
Terminal window
wheels dbmigrate latest
Terminal window
wheels dbmigrate down

Common column types available in migrations:

MethodDatabase TypeExample
t.string()VARCHAR(255)t.string(columnNames='email')
t.text()TEXTt.text(columnNames='bio')
t.integer()INTEGERt.integer(columnNames='age')
t.biginteger()BIGINTt.biginteger(columnNames='views')
t.decimal()DECIMALt.decimal(columnNames='price', precision=10, scale=2)
t.float()FLOATt.float(columnNames='rating')
t.boolean()BOOLEANt.boolean(columnNames='active')
t.date()DATEt.date(columnNames='birthdate')
t.datetime()DATETIMEt.datetime(columnNames='registeredAt')
t.time()TIMEt.time(columnNames='startTime')
t.binary()BLOBt.binary(columnNames='avatar')
t.timestamps()DATETIMECreates createdAt, updatedAt, and deletedAt (soft-delete marker)
  1. Descriptive Names: Use clear, action-oriented migration names
  2. One Change Per Migration: Keep migrations focused on single changes
  3. Always Include down(): Make migrations reversible when possible
  4. Use Transactions: Templates include transactions by default
  5. Test Rollbacks: Always test down() method works correctly
  6. Index Performance Columns: Add indexes on frequently queried columns
  7. Document Complex Migrations: Use description parameter for clarity
  8. Avoid Data in Migrations: Keep migrations schema-only when possible
function up() {
transaction {
try {
t = createTable(name = 'posts', force='false', id='true');
t.string(columnNames='title', allowNull=false);
t.text(columnNames='content');
t.references(name='userId', references='users');
t.boolean(columnNames='published', default=false);
t.timestamps();
t.create();
addIndex(table='posts', columnNames='userId');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}
function up() {
transaction {
try {
addColumn(table='users', columnType='string', columnName='phoneNumber', limit=20);
addColumn(table='users', columnType='boolean', columnName='emailVerified', default=false);
addColumn(table='users', columnType='datetime', columnName='lastLoginAt');
addIndex(table='users', columnNames='phoneNumber');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}
function up() {
transaction {
try {
// Add new column
addColumn(table='users', columnType='string', columnName='fullName');
// Migrate data (use with caution)
execute("UPDATE users SET fullName = CONCAT(firstName, ' ', lastName)");
// Remove old columns
removeColumn(table='users', columnName='firstName');
removeColumn(table='users', columnName='lastName');
} catch (any e) {
local.exception = e;
}
// Transaction handling...
}
}
Terminal window
wheels generate migration name=CreateUsersTable
# Error: Migration file already exists. Use force=true to overwrite.
# Solution:
wheels generate migration name=CreateUsersTable --force=true
Terminal window
wheels generate migration name="Create Users Table"
# Error: Invalid migration name. Use only letters, numbers, and underscores.
# Solution:
wheels generate migration name=CreateUsersTable
Terminal window
wheels dbmigrate latest
# Error during migration execution
# Check the migration file for syntax errors
# Review database logs
# Test the down() method
wheels dbmigrate down

Using templates provides several advantages:

Transaction Safety - All operations wrapped in transactions Error Handling - Automatic try/catch with rollback Consistency - Same structure across all migrations Best Practices - Templates follow Wheels conventions Customizable - Override templates in /app/snippets/ Maintainability - Centralized template updates