Skip to content

Command Line Tools

Code Generation

wheels generate is a dispatcher that routes to a family of generator subcommands. Each subcommand writes one or more files into your project: a model CFC, a controller with views, a migration, a full scaffold, a helper, a test spec, an admin interface, or a reusable code snippet. The dispatcher itself does no generation — it parses the first positional argument as a <type> and forwards the rest to the matching generator.

You'll use this for:

  • Scaffolding a complete resource (model + controller + views + migration + tests + route) in one command.
  • Producing individual artifacts — a blank migration, a property-add migration, a single controller — without touching anything else.
  • Stamping out common patterns (authentication, soft delete, API controller) as starting-point code you edit in place.
  • Turning an existing model into a CRUD admin interface via runtime introspection.
wheels generate <type> <name> [attributes...] [flags]

The g alias is supported for every form (wheels g model User). wheels create <type> exists as a forward-looking dispatcher but currently only accepts type=app, which forwards to wheels new; use wheels generate for everything else.

Running wheels generate with no arguments prints the list of supported types and one-line examples. Unknown types print Unknown generator type: <type> and exit without writing anything.

| Type | Alias | Writes | |---|---|---| | app | a | New project directory (delegates to wheels new) | | model | m | Model CFC and, if properties given, a create-table migration | | controller | c | Controller CFC and a view file per non-mutation action | | view | v | Single view template | | migration | migrate | Blank migration CFC | | scaffold | s | Model + migration + controller + views + tests + resource route | | api-resource | api | Model + JSON controller + migration + tests + namespaced route (no views) | | route | r | Adds a .resources() line to config/routes.cfm | | test | — | Model or controller spec file | | property | prop | Add-column migration for an existing model | | helper | h | Helper CFC under app/helpers/ | | snippets | — | Copies a named code pattern into the project | | admin | — | CRUD admin controller + views for an existing model (requires a running server) | | auth | — | Full authentication scaffold on the wheels.auth primitives (session, token, or JWT strategy) |

Property-aware generators — model, scaffold, api-resource, and property — accept a trailing list of name:type property pairs after the resource name. They also recognise --belongsTo=, --hasMany=, and --hasOne= flags for associations. Type names are normalised to Wheels migration column types — unknown types fall back to string.

The other generators (controller, view, migration, route, test, helper, snippets, admin) take their own positional arguments — see the per-subcommand Synopsis sections below for what each accepts.

| You write | Column type | Notes | |---|---|---| | name | string | Type defaults to string when :type is omitted. | | title:string | string | Also: varchar. | | body:text | text | Also: longtext. | | count:integer | integer | Also: int. | | views:biginteger | bigInteger | Also: bigint. | | price:decimal | decimal | Also: float, numeric. | | active:boolean | boolean | Also: bool. | | publishedAt:datetime | datetime | Also: timestamp. | | publishedOn:date | date | | | startsAt:time | time | | | payload:binary | binary | | | --belongsTo=author | — | Adds belongsTo("author") and an authorId FK column. | | --hasMany=comments | — | Adds hasMany(name="comments"). Pass multiple names comma-separated. | | --hasOne=profile | — | Adds hasOne(name="profile"). |

Generate a model CFC and, when properties are supplied, a matching create-table migration.

wheels generate model <Name> [property:type ...] [--belongsTo=<list>] [--hasMany=<list>] [--hasOne=<list>]

Writes app/models/<Name>.cfc with belongsTo/hasMany/hasOne associations inserted into config() based on the flags you passed. If any property:type arguments are present, the generator also stamps out a timestamped migration under app/migrator/migrations/ that creates the table. No migration is written when you generate a model with no properties.

Terminal window
wheels generate model User name email:string active:boolean --hasMany=posts

Writes app/models/User.cfc with hasMany(name="posts") in config(), plus a migration that creates the users table with name, email, and active columns (and the standard timestamps() trio).

Generate a controller CFC and view files for each read-only action.

wheels generate controller <Name> [action ...]

Writes app/controllers/<Name>.cfc with a stub method per action name you pass. For each action that isn't create, update, delete, or destroy, the generator also writes app/views/<name>/<action>.cfm. Passing no actions creates an empty controller with no view files. Action names may be space-separated or comma-separated — index,show is equivalent to index show.

Terminal window
wheels generate controller Users index show new create

Writes app/controllers/Users.cfc with index(), show(), new(), and create() methods, plus app/views/users/index.cfm, app/views/users/show.cfm, and app/views/users/new.cfm. The create action does not get a view.

Generate a single view template.

wheels generate view <controller> <action>

Writes app/views/<controller>/<action>.cfm. Both arguments are required — unlike generate controller, this subcommand does not fall back to a default action.

Terminal window
wheels generate view products edit

Writes app/views/products/edit.cfm.

Generate a blank migration file.

wheels generate migration <Name>

Writes app/migrator/migrations/<timestamp>_<Name>.cfc with empty up() and down() methods. The timestamp is generated at run time so sequential invocations produce a deterministic order. Use this when you want to hand-write schema changes that don't map to the property-based model/scaffold/property generators.

Terminal window
wheels generate migration AddIndexesToPosts

Writes a file such as app/migrator/migrations/20260420120000_AddIndexesToPosts.cfc.

Generate a complete CRUD resource.

wheels generate scaffold <Name> [property:type ...] [--belongsTo=<list>] [--hasMany=<list>]

The most opinionated generator. It runs, in order: model, create-table migration, controller (pluralised from <Name>), views (index, show, new, edit, _form), a model spec and controller spec under tests/specs/, and a .resources() route in config/routes.cfm. If any step fails the whole batch is rolled back so you're not left with a half-generated resource.

belongsTo associations automatically add a foreign-key column — --belongsTo=author adds an authorId integer column to the migration even if you didn't list it in the properties.

After running, do wheels migrate latest to apply the migration, then wheels start to try the new resource.

Terminal window
wheels generate scaffold Post title body:text publishedAt:datetime --belongsTo=author

Writes:

  • app/models/Post.cfc with belongsTo("author")
  • app/migrator/migrations/<timestamp>_create_posts_table.cfc with title, body, publishedAt, authorId, and the timestamps() trio
  • app/controllers/Posts.cfc with the seven CRUD actions
  • app/views/posts/index.cfm, show.cfm, new.cfm, edit.cfm, _form.cfm
  • tests/specs/models/PostSpec.cfc, tests/specs/controllers/PostsControllerSpec.cfc
  • .resources("posts") appended to config/routes.cfm

See the Database guide for what to do with the generated migration.

Generate a JSON-only resource — model, controller, migration, tests, and a namespaced route — without any views.

wheels generate api-resource <Name> [property:type ...] [--belongsTo=<list>] [--hasMany=<list>]

Same pipeline as scaffold, but the controller is generated in JSON-only mode (no view rendering), no view files are written, and the route is added under the api namespace using .namespace("api").resources(name="<names>", except="new,edit"). Use this when you're building a pure API — a separate front end, a mobile client, or a service-to-service endpoint.

Terminal window
wheels generate api-resource Product name price:decimal sku:string

Writes a JSON controller at app/controllers/api/Products.cfc, a create-table migration, and tests. After migrating and starting the server, curl http://localhost:8080/api/products.json returns an empty list.

Add a single resource route to config/routes.cfm.

wheels generate route <name>

Opens config/routes.cfm, checks that .resources("<name>") isn't already present, and inserts it into the mapper chain. Duplicate routes are detected by literal substring match — generate route posts is a no-op if .resources("posts") already appears in the file. If the mapper block can't be found, the command prints the line to add manually and exits.

This is the one-line equivalent of the route step that scaffold and api-resource run for you.

Terminal window
wheels generate route comments

Adds .resources("comments") to the mapper() chain in config/routes.cfm.

Generate a BDD test spec file.

wheels generate test <type> <Name>

<type> must be model or controller. Any other value is rejected.

Writes tests/specs/models/<Name>Spec.cfc or tests/specs/controllers/<Name>ControllerSpec.cfc, extending wheels.WheelsTest with a run() method and one placeholder it() block. Use this when you want a spec file for a model or controller that wasn't scaffolded.

Terminal window
wheels generate test model User

Writes tests/specs/models/UserSpec.cfc.

Generate an add-column migration for an existing model.

wheels generate property <ModelName> <property:type>

Writes a Add<Property>To<Tables> migration under app/migrator/migrations/. The migration calls changeTable() with the mapped column type in up() and removeColumn() in down(). Property type mapping follows the same table as the attribute syntax.

The generator prints a reminder to add a matching validatesPresenceOf() call in the model's config() — it does not modify the model CFC for you.

Terminal window
wheels generate property User phoneNumber:string

Writes app/migrator/migrations/<timestamp>_AddPhoneNumberToUsers.cfc with t.string(columnNames="phoneNumber") in up().

Generate a helper CFC under app/helpers/.

wheels generate helper <name> [functionName ...] [--force]

Writes app/helpers/<Name>Helper.cfc (the Helper suffix is appended automatically) with a stub method per function name you pass. Without any function names, an empty helper is created. The --force flag overwrites an existing file; without it, the command exits with an error if the file already exists.

Terminal window
wheels generate helper Formatting truncateText formatCurrency

Writes app/helpers/FormattingHelper.cfc with empty truncateText() and formatCurrency() methods.

Stamp out a named code pattern.

wheels generate snippets [pattern] [--force]
wheels generate snippets templates [--force]

Running with no arguments prints the registry of available patterns. Running with a pattern name writes one or more files into the project — exactly which files depends on the pattern. Re-running a pattern whose files already exist is a no-op; pass --force to overwrite.

The special templates subcommand copies every raw generator template from the CLI's bundled directory into app/snippets/ so you can customise them for your project. Regular named patterns write curated, opinionated starting code instead.

| Pattern | What it writes | |---|---| | auth | app/controllers/Sessions.cfc, app/views/sessions/new.cfm, app/snippets/auth-filter.cfm | | soft-delete | app/snippets/soft-delete.cfm, app/snippets/soft-delete-migration.cfc | | api-controller | app/snippets/api-controller.cfc | | crud-controller | app/snippets/crud-controller.cfc | | flash-messages | app/views/shared/_flash.cfm | | pagination | app/snippets/pagination-view.cfm | | seed-data | app/snippets/seeds.cfm, app/snippets/seeds-development.cfm | | mailer | app/snippets/user-mailer.cfc |

Terminal window
wheels generate snippets auth

Writes a session controller, a login view, and an auth filter. Edit the filter and add filters(through="authenticate") to the controllers that need protection.

Generate a CRUD admin interface for an existing model.

wheels generate admin <ModelName> [--force] [--no-routes]

Unlike the other generators, this one requires a running dev server — it hits http://localhost:<port>/wheels/cli?command=introspect&model=<ModelName> to read the model's properties, associations, and validations, then generates a controller and views that reflect the live schema. Start the server with wheels start first.

| Flag | Default | Description | |---|---|---| | --force | off | Overwrite existing admin controller and view files. | | --no-routes | off | Skip adding the admin route to config/routes.cfm. |

Terminal window
wheels start
wheels generate admin User

After reload, the admin interface is available at /admin/users.

Generate a complete authentication scaffold on the built-in wheels.auth primitives (PasswordHasher, Authenticator, and the session/token/JWT strategies).

wheels generate auth [ModelName] [--model=User] [--strategy=session|token|jwt] [--registration|--no-registration] [--force]

| Flag | Default | Description | |---|---|---| | --model=<Name> | User | Model (and table, pluralised) to generate. A bare positional name works too. | | --strategy=<name> | session | session (browser login), token (opaque API bearer tokens), or jwt (stateless signed tokens). | | --registration / --no-registration | on | Include the public sign-up flow (session strategy only). | | --force | off | Overwrite existing generated files and regenerate the injected config blocks in place. |

The generated code is code you own — every file carries a stamped header, nothing is patched by framework upgrades. To pick up generator improvements later, re-run with --force on a clean branch and review the changes with git diff. The route, service, and strategy registrations are injected between // wheels:generate-auth:* marker comments and replaced in place on re-runs, never duplicated. The migration is never overwritten, even with --force.

All strategies share the same User model: PBKDF2-HMAC-SHA256 hashing through the passwordHasher service (a transient password property is validated — presence on create, 12-character minimum, confirmation — then hashed into passwordDigest and scrubbed in a beforeSave callback), an authenticate() method with transparent rehash-on-login, and single-use SHA-256-digested password reset tokens that expire after 2 hours.

Session (default) writes Sessions/Passwords/Registrations controllers (every config() starts with super.config(), so the base controller's CSRF protection stays active), startFormTag-based views, login/logout/register/password routes, and DI registrations for passwordHasher, authenticator, and sessionStrategy in config/services.cfm, with the strategy wired into the authenticator in app/events/onapplicationstart.cfm.

Token writes app/controllers/api/Sessions.cfc instead (no views; the registration flag doesn't apply): POST /api/session exchanges credentials for an opaque bearer token returned exactly once — only its SHA-256 digest is stored — and DELETE /api/session revokes it. The TokenStrategy validator is registered at startup and resolves accounts by token digest.

JWT writes an api/Sessions.cfc that mints tokens with JwtService, signed with the WHEELS_JWT_SECRET environment variable (at least 32 random bytes). Startup fails loudly when the secret is missing or too short. JWTs have no server-side revocation — an issued token stays valid until it expires; use --strategy=token if you need instant revocation.

Both a model spec and a sessions-controller spec are generated under tests/specs/ so the scaffold is covered by wheels test from day one.

Terminal window
wheels generate auth
wheels migrate latest
wheels start

Writes:

  • app/models/User.cfc with hashing, validations, and authenticate()
  • app/migrator/migrations/<timestamp>_create_users_table.cfc with a unique email index
  • app/controllers/Sessions.cfc, Passwords.cfc, Registrations.cfc
  • app/views/sessions/new.cfm, registrations/new.cfm, passwords/new.cfm, passwords/edit.cfm
  • Marked blocks in config/routes.cfm, config/services.cfm (created if absent), and app/events/onapplicationstart.cfm
  • tests/specs/models/UserAuthSpec.cfc, tests/specs/controllers/SessionsControllerSpec.cfc

After migrating and restarting, /login and /register are live. See the authentication patterns guide for how the pieces fit together and how to protect actions with a filter.

Terminal window
wheels generate scaffold Post title body:text publishedAt:datetime
wheels migrate latest
wheels start

Generates the model, migration, controller, views, tests, and route; migrates the database; and boots the server. /posts is live.

Terminal window
wheels generate model Comment body:text --belongsTo=post

Creates Comment.cfc with belongsTo("post"), plus a migration that includes a postId foreign-key column. Useful when you want the association wired up but don't need a controller or views yet.

A blank migration for a hand-written schema change

Section titled “A blank migration for a hand-written schema change”
Terminal window
wheels generate migration AddUniqueIndexOnUsersEmail

Use this when the change is something the property-based generators can't express — adding a composite index, renaming a column, seeding reference data.

Terminal window
wheels generate api-resource Product name price:decimal
wheels migrate latest

No views, no HTML controllers — just a JSON endpoint mounted under /api/products.json.