Skip to content

Start Here

Anatomy of a Wheels App

This page is the map of an app created with wheels new: every file and folder it creates, and what each is for. If you’re coming from Wheels 2.x, the biggest change is that the framework moved from a wheels/ folder at the root into vendor/wheels/, and the web root moved to public/.

myapp/
app/ Your application code
controllers/ Controllers (plural PascalCase: Users.cfc)
Controller.cfc Base class your controllers extend
Main.cfc The welcome page's controller
Up.cfc Health check at /up (used by `wheels deploy`)
models/ Models (singular PascalCase: User.cfc)
Model.cfc Base class your models extend
views/ One folder per controller
layout.cfm The default layout
helpers.cfm View helpers available in every view
main/index.cfm The welcome page
events/ Application and request lifecycle hooks (onapplicationstart.cfm, onerror.cfm, …)
global/functions.cfm Global user-defined functions
jobs/ Background jobs (extend wheels.Job)
lib/ Plain CFCs: services, value objects, API clients
mailers/ Mailers
plugins/ Legacy plugin folder (deprecated; use packages)
migrator/migrations/ Database migration CFCs
policies/Policy.cfc Base class for authorization policies
snippets/ Templates the generators write code from
config/ Configuration, loaded at application start
app.cfm Application.cfc-level settings (this.name, datasources, …)
environment.cfm Which environment this deployment runs as
settings.cfm Shared settings: datasource, reload password, URL rewriting, …
routes.cfm Your routes (mapper() … .end())
services.cfm Dependency-injection registrations
db/ SQLite database files (development.sqlite, test.sqlite)
public/ THE WEB ROOT: the only folder a browser can reach
Application.cfc Bootstraps the app: defines mappings, loads .env
index.cfm Single entry point; hands every request to the dispatcher
urlrewrite.xml Rewrite rules for Tuckey
stylesheets/, javascripts/, images/, files/, miscellaneous/
tests/ Your test suite
specs/ models/, controllers/, functional/
runner.cfm The app's test runner
populate.cfm Test-database setup
vendor/ Third-party code; you don't edit anything in here
wheels/ THE FRAMEWORK ITSELF
<package>/ Packages added with `wheels packages add`
.env Environment variables and secrets (never commit)
.gitignore
lucee.json Server configuration for `wheels start`
rewrite.config URL-rewrite rules for `wheels start`
AGENTS.md, CLAUDE.md, .ai/ Docs for AI coding agents working in this app

The generated .gitignore excludes vendor/. Per-environment overrides go in config/<environment>/settings.cfm; wheels new doesn’t create those folders, so add one when you need it.

There is no magic registry and no generated wiring. public/Application.cfc defines six CFML mappings, all relative to the public/ folder:

this.mappings["/app"] = expandPath("../app/");
this.mappings["/vendor"] = expandPath("../vendor/");
this.mappings["/wheels"] = expandPath("../vendor/wheels/");
this.mappings["/tests"] = expandPath("../tests");
this.mappings["/config"] = expandPath("../config");
this.mappings["/plugins"] = expandPath("../plugins");

That’s the whole contract. Point any CFML-capable web server at public/ and the app can find its own code, no matter where on disk the folder sits or what tool put it there. It also means everything outside public/ — your models, your config, your .env, the framework itself — is unreachable from a browser.

  • app/ — everything you write. Controllers extend Controller, models extend Model, views execute in the controller’s variable scope. Conventions (naming, pluralization) are covered in Conventions over Configuration.
  • config/ — read once at application start. Changing anything here requires a reload (restart the app, or ?reload=true&password=… — see Working Without the CLI).
  • public/ — the web root and only the web root. Static assets are served directly; everything else routes through index.cfm to the dispatcher.
  • vendor/ — the framework plus any installed packages. Treat it as read-only: upgrades replace vendor/wheels/ wholesale, so local edits there are lost by design.
  • tests/ — your app’s test suite, run via wheels test, or without the CLI at /wheels/app/tests in development. See Testing.
  • db/ — only meaningful for SQLite setups; other databases live wherever your server keeps them. The .sqlite files are gitignored.
  • lucee.json / rewrite.config — the server and rewrite config wheels start uses. Another server setup ignores them.

Inside vendor/wheels/ two things deserve a mention even though you never edit them:

  • vendor/wheels/Injector.cfc — the built-in dependency-injection container. Wheels 4 does not depend on WireBox; if you remember downloading WireBox for Wheels 3, that requirement is gone.
  • vendor/wheels/wheelstest/ — the built-in test framework (wheels.WheelsTest). Wheels 4 does not depend on TestBox either; the TestBox-and-four-hidden-modules scavenger hunt from the 3.0 era is over.

Both facts matter to one specific reader: the person assembling an app from zips. The complete dependency list for a Wheels 4 app is: the framework. See Manual Installation.