Skip to content

Start Here

Part 1: Hello, Wheels

You’ll create a new Wheels app called blog, start the dev server, and add a /hello route that renders a custom page. By the end, curl localhost:8080/hello returns “Hello, World.”

You’ll learn:

  • What wheels new scaffolds
  • How to add a route, a controller action, and a view
  • How reload works

This is Part 1. You’re starting with nothing. By the end, you’ll have a working Wheels app responding to /hello. Each subsequent part begins with a “Where we left off” banner showing the file tree and database schema, so you can resume without reading Part N-1.

If you haven’t yet, see Installing Wheels for macOS, Windows, and Linux.

Verify:

Terminal window
wheels --version

You should see a Wheels version number. If not, revisit the install guide.

  1. Create a new app called blog. Run this in a fresh directory where you want the app to live:

    your shell
    wheels new blog

    Expected output: a configuration summary (port, datasource, reload password) and the Next steps block.

  2. Move into the new directory:

    your shell
    cd blog
  3. Start the dev server:

    your shell
    wheels start

    The server boots in a few seconds and prints the URL it’s listening on (default: http://localhost:8080).

Open http://localhost:8080 in your browser. You’ll see a welcome page: “Welcome to blog. Your Wheels application is running.”

  • Directoryblog
    • Directoryapp
      • Directorycontrollers
        • Main.cfc
      • events
      • global
      • jobs
      • lib
      • mailers
      • Directorymigrator
        • migrations
      • models
      • plugins
      • snippets
      • Directoryviews
        • helpers.cfm
        • layout.cfm
        • Directorymain
          • index.cfm
    • Directoryconfig
      • app.cfm
      • environment.cfm
      • routes.cfm
      • settings.cfm
    • Directorydb
      • development.sqlite
      • test.sqlite
    • Directorypublic
      • Application.cfc
      • index.cfm
      • urlrewrite.xml
      • files
      • images
      • javascripts
      • miscellaneous
      • stylesheets
    • rewrite.config
    • Directorytests
      • Directoryspecs
        • controllers
        • functional
        • models
    • Directoryvendor
      • wheels
    • .env
    • .gitignore
    • lucee.json

Three directories matter right now:

  • app/controllers/ — request-handling code. You’ll see the default Main.cfc.
  • app/views/ — templates. The default home page lives at app/views/main/index.cfm.
  • config/routes.cfm — maps URLs to controller actions.

vendor/wheels/ is the framework itself, copied into your app. You generally don’t touch it.

The other top-level app/ directories (events/, global/, jobs/, lib/, mailers/, migrator/, models/, plugins/, snippets/) are scaffolding for features you’ll meet later — background jobs, mailers, application events, model definitions, generator templates. Leave them alone for now. The remaining root-level files are framework configuration: lucee.json configures the Lucee runtime, .env holds environment variables (the dev server’s reload password lives here), and config/app.cfm plus the rest of config/ configure the framework itself.

You’ll add a new URL, a new action on the existing Main controller, and a new view.

  1. Open config/routes.cfm. Inside the <cfscript>...</cfscript> block, you’ll find a mapper()...end() chain. It looks like this:

    config/routes.cfm — starting state
    mapper()
    // CLI-Appends-Here
    .wildcard()
    .root(to="main##index", method="get")
    .end();

    Add a .get(...) line for /hello before .wildcard():

    mapper()
    .get(name="hello", pattern="/hello", to="main##hello")
    .wildcard()
    .root(to="main##index", method="get")
    .end();

    .get(...) declares: when a GET hits /hello, run the hello action on the Main controller. Route name hello means you can generate this URL elsewhere with urlFor(route="hello").

  2. Open app/controllers/Main.cfc and add a hello action alongside index:

    component extends="Controller" {
    function index() {
    }
    function hello() {
    }
    }

    The action does nothing visible. Wheels will still render the view at app/views/main/hello.cfm by convention.

  3. Create app/views/main/hello.cfm:

    app/views/main/hello.cfm
    <h1>Hello, World</h1>
  4. Tell Wheels to pick up your changes:

    your shell
    wheels reload

Your app should now respond to /hello:

verify
curl -s http://localhost:8080/hello

Expected: <h1>Hello, World</h1> in the response body (plus any layout wrapping).

wheels: command not found — PATH doesn’t see the CLI. See Installing Wheels.

Route '/hello' not found — you added the route but didn’t reload. Run wheels reload.

Blank page at /hello — the view file is in the wrong place. It must be at app/views/main/hello.cfm (lowercase main, matching the controller name).

In Part 2 you’ll add your first model, write a migration, and seed real data. The browser-render-a-page part is done; now you meet the database.