All tags / #wheels-4

#wheels-4

60 posts

Validation conditions now fail closed: migrating your 2.x condition strings

Peter Amiri

Part 5 of "From 2.x to 4.x: the field guide". CFWheels 2.x ran validation condition= and unless= strings through Evaluate(); Wheels 4.1 parses a small grammar and throws Wheels.InvalidValidationCondition for what it can't parse. This post lists the grammar 4.1.2 accepts, runs ten 2.x-style strings on 4.1.2 (three throw, seven give the wrong answer without an error), rewrites each one, covers the this.method() escape hatch and the two production conditions from the 4.1.1 CHANGELOG, and explains what Wheels 4.2 changes.

The file the upgrade doesn't touch: `public/Application.cfc` from 4.0 to 4.1

Peter Amiri

Part 4 of "From 2.x to 4.x: the field guide" covers public/Application.cfc, the app-owned file that a vendor/wheels/ swap never updates. It replays every template change from 4.0.0 to 4.1.2 one commit at a time: the DI container, the onError fallbacks, the fail-closed reload gate, the X-Wheels-Reload-Password header, IP-based debug access, the session cookie, the Adobe teardown handlers, the jBCrypt load path and the .env parser. A table says what each edit fixes, which release added it and whether skipping it fails safe, and the post shows what changed on Lucee 7 and Adobe ColdFusion 2025 after each edit.

Pass 2: 3.x to 4.1 with `wheels upgrade check`

Peter Amiri

Part 3 of "From 2.x to 4.1: the field guide" takes the example app from Wheels 3.0.0+33 to 4.1.2. It reads the real `wheels upgrade check` output before and after, explains why to run the check before swapping vendor/wheels, and walks the 14 commits: the framework swap, the 4.1 public/Application.cfc, plugin replacements, paginationNav(), the CSRF settings, a RocketUnit-to-WheelsTest conversion and validation conditions. It also covers the breakers only a real boot found: default='' in migrations, structs assigned to scalar columns, dates interpolated into where strings on 4.1.2, and a reload password the CLI and the app disagreed on.

Pass 1: 2.x to 3.0, the layout move

Peter Amiri

Part 2 of "From 2.x to 4.1: the field guide" walks the nine commits that take the example app from CFWheels 2.2 to Wheels 3.0.0+33: moving code into app/ and assets into public/, the new public/Application.cfc, vendor/wheels pinned to 3.0.0+33, path and test fixes, and the problems the 3.0 guide doesn't mention: events/ has to move, plugins/ stays at the root, a caret range can install a 4.0 development build, joinType="left" silently drops a nested join, and a 2.3 validatesConfirmationOf change breaks login.

2.x to 4.x: why it's two passes, and the map

Peter Amiri

The first post in "From 2.x to 4.1: the field guide". Going from Wheels 2.x to 4.1 takes two passes: 2.x to 3.0.0+33 (the layout move), then 3.0 to 4.1 (the framework swap and the 4.x breaking changes). This post shows what each pass touches, which files no upgrade command ever touches, and the real CFWheels 2.2 app we upgrade on camera, wheels-dev/cfwheels-example-app, with one branch and one commit per step.

Wheels 4.1.2: deploy that builds and pushes, honest CLI exit codes, and four security fixes

Peter Amiri

Wheels 4.1.2 is a patch release on 4.1.x: `wheels deploy` now builds and pushes the image it deploys, failing CLI commands exit non-zero, migrations can opt out of the per-step transaction, a new `baseUrl` setting, migrator and model fixes, tighter gates on the development tools, and four security fixes. Upgrading is recommended; a few generated-app files need small edits.

Wheels 4.1.1 is out — a CLI security fix and the 4.1.0 papercuts

Peter Amiri

Wheels 4.1.1 fixes a CLI security issue (GHSA-x3cm-2j3q-jgg4), makes the first request after a restart work on Adobe ColdFusion, stops multi-database migrations from stalling, and clears out the rough edges people hit in 4.1.0. Every compatibility-matrix leg passed, test by test, before it shipped.

Wheels 4.1.0 is out — the release a bug report built

Peter Amiri

Wheels 4.1.0 is out: bcrypt, one-line session auth, pretty routes, DI factories, CLI diff/dry-run/offline, a security hardening pass, and model instantiation ~2.5x faster. Every one of those threads traces back to a single issue filed by @chapmandu — and the release is better because it didn't start with a features list.

Find your riskiest code: the 900-line controller and the tool that caught it

Peter Amiri

There was a controller with nine hundred lines and one bug that only fired in September. Three developers touched it that week. When we pointed the complexity panel at it, the score said everything — and the coverage run said the test suite had never touched it. 4.1 ships the two tools that make that obvious before the September bug goes out.

Fail closed, everywhere: the hardening pass and the assumptions that weren't true

Peter Amiri

A security review of Wheels 4.0 found a pile of "surely that's safe" that weren't: a policy that treated the string "yes" as true, a form method override that worked on a GET, a zip that could escape its directory. 4.1 closes them by failing closed. Here's the story area by area, and what you have to check on the way up.

2.5x faster, the long way: a performance story with a villain

Peter Amiri

@chapmandu reported his ported app's test suite running nearly six times slower on Wheels 4 than on 2.5. We didn't believe it, so we built a benchmark rig to prove him wrong. He was right. Here's the three-month investigation, two unrelated bugs it surfaced, and the lesson about trusting micro-benchmarks.

The CLI that stopped guessing: diff, dry-run, and offline

Peter Amiri

A deploy pipeline went green while doing nothing to the database, and a code review approved a generator run that had already written files. Both were "the CLI guessed wrong." 4.1 adds diff, dry-run, and offline mode — and the honesty fixes that make a green build mean something.

DI factories with toFactory: the service that couldn't decide

Peter Amiri

A storage service had to pick S3 in production and the local disk in dev. The to() binding couldn't express that, so the decision leaked into every caller. toFactory() gives a DI container a *how* — and it's the difference between one closed arrow and ten open switch statements.

Pretty URLs with bindBy: the unshareable link

Peter Amiri

A support engineer pastes a link into a customer chat, and they get /posts/4821. Nobody shares /posts/4821. bindBy="slug" exists to make resource URLs say something — and it comes with one mistake you can make that quietly breaks your site.

One-line session auth, and the three bugs you won't find the hard way

Peter Amiri

The enableSession() facade is one line of config. But the real story is the three subtle bugs it exists to prevent — the ones that only surface after the second deploy, or only on a reload, or only for a user who was already logged in. Here's what happens when the wiring goes right.

bcrypt for your passwords: the post that starts with a pentest report

Peter Amiri

The story of how bcryptHash(), bcryptVerify(), and bcryptNeedsRehash() made it into Wheels 4.1 starts with a pentest finding and a migration that took three weeks. Here's the helpers, the cost trade-off, and the part where Adobe CF made us question our sanity.

Wheels 4.1 is coming — and this release has a story

Peter Amiri

Wheels 4.1 isn't another "we added features and fixed bugs" release. It's the release where a single bug report sent us back to the drawing board, where a security pass made the things you always assumed were safe actually be safe, and where the framework got meaningfully faster. Here's what's coming, and the story behind it.

Wheels 4.0.6: generate auth, policies, and storage — plus the mixin-cache speed fix

Peter Amiri

Wheels 4.0.6 is the first 4.0 feature release after 4.0.5's hardening pass — one-command authentication (`wheels generate auth`), an authorization policy layer, pluggable local/S3 storage disks, query-builder chains that can start with `select()`, and the mixin-plan cache that was making 4.0.x feel slow. Also: Adobe `onApplicationEnd` teardown, tenant/pagination request-key collisions, test-runner isolation, and a production gate on `/wheels`.

Internationalization in an Afternoon: wheels-i18n

Peter Amiri

i18n usually arrives as an emergency. wheels-i18n makes the mechanical part boring: t() and tp() with interpolation and pluralization, JSON or database catalogs, session-based locale switching, and the fallback chain that makes incremental translation actually shippable.

The Debug Bar: A Complete Tour of Wheels 4's Development Cockpit

Peter Amiri

Most developers use a fifth of the debug bar and writeDump() around the rest. The full tour: what each panel answers, the developer tools behind the last tab, the throughput cost worth knowing, and why the bar can't follow you to production — by design.

Calculated Properties: SQL-Defined Values Without the N+1 (or the Bloat)

Peter Amiri

Declare fullName or commentCount once as SQL on the model and it behaves like a column everywhere — sortable, selectable, never stale. And Wheels 4's select=false + includeCalculated pair fixes the cost problem calculated properties always created: expensive expressions run only where they're asked for, additively.

wheels console: The Debugging Move You're Not Using

Peter Amiri

A REPL inside your running app: models resolve, scopes chain, the DI container answers, settings read back. The sessions that replace writeDump-and-refresh archaeology, the slash-commands, and why console and repl are different tools that look alike.

Channels: Pub/Sub for Wheels 4, No Extra Infrastructure

Peter Amiri

SSE gives you the pipe; channels answer the harder question — how the model callback that knows something happened reaches the connection three requests away. publish(), subscribeToChannel(), channelSSETag(), the memory-vs-database adapter decision, and the named-event gotcha that catches almost everyone.

Processing 400,000 Rows Without Melting the JVM: findEach and findInBatches

Peter Amiri

findAll on 400,000 rows materializes everything into memory at once — and the request cache holds it there. Wheels 4's findEach and findInBatches walk big result sets a page at a time, with three built-in optimizations you'd forget to hand-roll and two sharp edges worth knowing first.

Running Wheels 4 the CommandBox Way

Peter Amiri

CommandBox teams do not have to switch. The complete box-native workflow for Wheels 4 — install from ForgeBox, engine selection with cfengine, datasources via CFConfig, every framework task's in-app equivalent, CI with setup-commandbox, and an honest accounting of what the wheels CLI adds if you ever want it.

How Wheels Actually Reads Your Configuration

Peter Amiri

Why exporting WHEELS_ENV=production doesn't flip your app into production — and everything else about the config pipeline: the three-file load order, the .env chain that shadows OS variables, what production mode really changes, and the reload gate's fail-closed mechanics.

Request IDs, /up, and Logs a Machine Can Read: Observability in Wheels 4

Peter Amiri

Three small pieces of plumbing turn 'the site gave me an error around lunchtime' into a single grep: the RequestId middleware, the /up health endpoint your app already ships, and a forty-line JSON logging pattern. Plus the two health-endpoint mistakes everyone makes once.

CORS in Wheels 4: Deny by Default, Open It Correctly

Peter Amiri

Wheels 4 denies cross-origin requests until you say otherwise — and the Cors middleware refuses the popular ways of saying it wrong. The deny-all breaking change, single-origin resolution, Vary: Origin, preflight handling, and the legacy-settings trap the framework defuses for you.

One Authenticator, Three Doors: Sessions, Tokens, and JWTs in Wheels 4

Peter Amiri

Wheels 4 ships session, API-token, and JWT authentication as pluggable strategies behind one Authenticator registry. Wiring up all three doors — with the sharp edges: private filters, dynamic finders, the 32-byte JWT secret minimum, and the inline-closure constructor that crashes Adobe ColdFusion.

Wheels 4 Without the Tooling: Two Zips, Zero Dependencies

Peter Amiri

The manual install that took the community two days to reverse-engineer in Wheels 3 is now two zip files and zero external dependencies. How to install, migrate, test, seed, and upgrade a Wheels 4 app with no CLI, no CommandBox, and no package manager — plus the new guides that document every step.

Sending Email in Wheels 4.0: sendEmail, Templates, and Background Delivery

Peter Amiri

A complete how-to for sending email in Wheels 4.0 — the sendEmail controller helper, how email view templates resolve, multipart text+html, attachments, and the correct way to deliver mail from a background job. Includes the sharp edges that actually bite.

File Uploads in Wheels 4.0, the Idiomatic Way

Peter Amiri

Wheels 4.0 has no ActiveStorage-style attachment system — and that's fine. This how-to walks the honest idiomatic upload flow end to end: fileField/fileFieldTag form helpers, the startFormTag multipart trap, native cffile in the controller, model validation over the result, and traversal-safe downloads with sendFile.

Caching in Wheels 4.0: Actions, Queries, Partials, and Invalidation

Peter Amiri

A practical, source-grounded tour of every cache Wheels 4.0 actually ships — action caching, query caching, partial and page caching — plus the honest truth about invalidation: there's no clearCache(key), so you live on short expiry times and full reloads.

Associations Deep-Dive: hasMany, belongsTo, Nested Properties, and Eager Loading

Peter Amiri

A comprehensive Wheels 4.0 guide to model associations — hasMany, belongsTo, and hasOne, the exact dynamic methods each one generates, dependent-delete cascades, nested properties from form params, eager loading with include=, has-many-through, and polymorphic associations — with the sharp edges that bite in practice.

Form Helpers in Wheels 4.0: Object Forms, HTML5 Fields, and Error Rendering

Peter Amiri

A worked how-to on Wheels 4.0 form helpers: object-bound fields that round-trip into params.user.email, the real HTML5 single-input helpers, and the two error-rendering helpers — plus the sharp edges that bite first.

Validations Beyond Presence: Conditional Rules, Custom Validators, and Enum Integration

Peter Amiri

A practical Wheels 4.0 guide to the full validation surface — conditional rules with condition/unless/when, custom validate() methods, enum() integration, and how to read the error API correctly. Built end-to-end around one User model, with every behavior grounded in the framework source.

Routing Deep-Dive: Resources, Nested Callbacks, and Route Model Binding

Peter Amiri

A complete Wheels 4.0 routing guide — RESTful resources, nested resources via callback vs manual nested=true, and route model binding that resolves params.post before your action runs. Plus the sharp edges: route order, the protected-helper-names trap, and how a missing record 404s.

Seeding Your Database the Idempotent Way

Peter Amiri

Wheels 4.0 ships a convention-based seeder built around seedOnce() — an idempotent helper that creates a record only if a matching one doesn't already exist, wrapped in a transaction that rolls the whole run back on any failure. This is the worked guide: file conventions, the CLI, composite uniqueness, and the sharp edges.

Dependency Injection in Wheels 4.0: services.cfm, Scopes, and Auto-Wiring

Peter Amiri

A user-facing how-to for the Wheels 4.0 DI container: register services in config/services.cfm with singleton/request/transient scopes, resolve them anywhere with service(), declare them in controllers with inject(), and let constructor auto-wiring fill in the rest.

Writing Your Own Middleware in Wheels 4.0

Peter Amiri

A from-scratch guide to authoring custom middleware in Wheels 4.0 — the one-method interface, the singleton-per-app lifecycle and the thread-safety it demands, how to short-circuit, and how to register globally or per route.

Pagination That Isn't a Pain: paginationNav and the Preset System

Peter Amiri

A complete how-to on Wheels 4.0's paginationNav helper — the all-in-one nav, the bootstrap5/bootstrap4/tailwind preset system, the auto/always/never anchor tri-state, and the dev-time guardrails that catch typos before they ship.

Real-Time Without WebSockets: Server-Sent Events in Wheels 4.0

Peter Amiri

A worked guide to Wheels 4.0's Server-Sent Events helpers — renderSSE for one-shot events, the initSSEStream/sendSSEEvent/closeSSEStream trio for long-lived streams, plus isSSERequest content negotiation, keep-alive heartbeats, and the engine-fragility sharp edges to test for.

Beyond findAll: Scopes, Enums, and the Chainable Query Builder

Peter Amiri

Wheels 4.0 ships three model-side features that compose into one design: scopes name reusable query fragments, enums turn property values into auto-generated checkers and scopes, and the chainable query builder swaps raw WHERE strings for fluent, type-aware composition. This post walks all three through one worked example and surfaces the bug I hit while writing it.

Wheels 4.0.5: a hardening release — 100+ fixes across security, performance, and deploy, now installable anywhere

Peter Amiri

Wheels 4.0.5 is out, and together with 4.0.4 it's one of the most substantial hardening passes on the 4.0 line — 100+ changes spanning security (open-redirect and info-disclosure fixes, fail-closed gates, SQL-surface tightening), warm-path performance, a much tougher `wheels deploy`, and Adobe CF / BoxLang cross-engine fixes. 4.0.5 then makes the whole thing installable the same way on every major platform — Homebrew, Scoop, apt, dnf — including arm64 Linux, verified daily.

Wheels + Claude: Building a Feature via the stdio MCP

Peter Amiri

Wheels 4.0 ships a stdio MCP server that exposes the CLI to AI editors — not as a chat sidekick, but as a tool surface a model can call. This post walks the architecture (reflection over a single CFC), builds a commenting feature end-to-end via Claude, and is honest about the config-template drift I hit while writing it.

Migrations That Survive a Team: the Wheels 4.0 Migrator

Peter Amiri

A broad how-to on the Wheels 4.0 migrator: write up()/down() migrations, drive the TableDefinition column builder (including the three-column timestamps() and the columnNames-preferred helpers), seed with inline SQL, and run the migrate CLI — including the doctor/info/forget/pretend commands that keep a shared dev database honest.

Anatomy of a Wheels Package

Peter Amiri

Wheels 4.0 replaces plugins with a package system that treats your filesystem as the registry. This post takes a small package from empty directory to installed-and-mixed-in, names every field in the manifest, and is honest about the rough edges I hit while writing it.

Wheels 4.0.3: rebuilt CLI argument parsing, honest exit codes, and wrong-database guardrails

Peter Amiri

Wheels 4.0.3 is the third patch on the 4.0 line, focused on making the `wheels` CLI trustworthy in scripts and CI: argument parsing is rebuilt end-to-end (`--no-*` negations and named-only flags finally reach every command), failures exit non-zero, and write-side commands refuse to attach to a different project's server. Plus PostgreSQL/CockroachDB foreign-key migration fixes, pre-23c Oracle support, preserved column casing in model output, and a fix that stops framework helpers from being URL-invokable as controller actions.

Wheels 4.0.2: shared-database migration reconciliation and native apt/yum repos

Peter Amiri

Wheels 4.0.2 is the second patch on the 4.0 line. It teaches the migrator how to cope with a database that more than one developer shares — orphan-version detection, a `migrate doctor` health report, and `forget` / `pretend` reconciliation commands — fixes a class of silent migration rollbacks on MSSQL, makes the migrator's column-name helpers consistent, and ships native signed apt.wheels.dev / yum.wheels.dev package repositories so Linux installs and upgrades are a one-liner.

Wheels 4.0.1: Adobe CF hardening, Windows Scoop fixes, and the post-GA shakeout

Peter Amiri

Wheels 4.0.1 is out — the first patch on the 4.0 line. It hardens Adobe ColdFusion 2023/2025 compatibility, fixes the Windows Scoop install regressions reported after GA, adds CSS-framework presets to paginationNav(), short-circuits whereIn([]) so empty filters stop emitting invalid SQL, and threads about a hundred smaller fixes through the rest of the framework.

Skip the Plugin: Building a Rate-Limited API in Wheels 4.0

Peter Amiri

Wheels 4.0 ships a real middleware pipeline, and three of the things you used to reach for a plugin to add — rate limiting, CORS, and security headers — are built in. This post walks through wiring a JSON API with per-API-key rate limits, sane proxy handling, and the right ordering so you do not accidentally weaken the very headers you were trying to add.

From WireBox to wheelsdi — The Framework Gets Leaner

Peter Amiri

Wheels 4.0 decomposes the framework's rim against the CFML engine, pulls dependency injection and the test runner in-house as wheelsdi and WheelsTest, and breaks the monolithic boot sequence into discrete phases. Nothing a user of the framework notices at the surface. Everything a contributor notices when they try to add the next feature.

Upgrading from Wheels 3.x

Peter Amiri

Wheels 4.0 lands with seven breaking changes and a Legacy Compatibility Adapter for teams that cannot touch every call site this quarter. This post is the honest map: what breaks, how to detect it, how to fix it, and when the adapter is the right answer instead.

Wheels 4.0: Closing the Maturity Gap

Peter Amiri

For years, the same rows showed up red in every framework comparison: no bulk ops, no polymorphic associations, no advisory locks, no middleware, no browser testing. Wheels 4.0 closes those gaps. This is a guided tour of what changed and where the framework still trails.

LuCLI and the Zero-Docker Developer Experience

Peter Amiri

Wheels 4.0 ships with a new inner-loop story: a single native binary, a sixty-second full test run, and a multi-phase migration that quietly moved the framework off Docker for day-to-day development. This post walks through how we got here, what LuCLI is, and why cross-engine matrix testing still belongs in Docker.

Multi-Tenancy Built In

Peter Amiri

Wheels 4.0 lifts multi-tenancy out of plugin territory and into the framework core. Per-request datasource switching catches every query and every job on the way to the database, and tenant-aware background jobs come along for the ride.

Background Jobs Without Redis

Peter Amiri

Wheels 4.0 ships a production-ready job queue that needs only your database. You get a persistent CLI worker, live monitoring, configurable backoff, and tenant-aware enqueueing without any extra services. Redis still wins for very high throughput and pub/sub fan-out — this post names where the DB-backed queue is the right call and where it is not.

Testing in Wheels 4.0

Peter Amiri

Wheels 4.0 completes the test pyramid: BDD unit tests, a fluent HTTP TestClient for integration, and Playwright-powered browser automation for end-to-end — all running in parallel. The category that was most embarrassing in 3.0 is the most complete in 4.0.

Security Hardening in Wheels 4.0

Peter Amiri

Wheels 4.0 shipped more than forty security-hardening pull requests across eight categories — SQL, path handling, session integrity, CORS, rate limiting, auth and dev surfaces, CLI and MCP, and view helpers. The common thread is a shift in posture: the framework's defaults are now safe first, convenient second.

Porting Kamal to CFML: How wheels deploy Ships 4.0 Apps Without Ruby

Peter Amiri

Wheels 4.0 ships with a new command: wheels deploy. It's a port of Basecamp's Kamal into the Wheels CLI — zero-downtime Dockerized deploys to Linux servers over plain SSH, no Ruby runtime required. This post covers what got ported, the one deliberate divergence from Kamal, and the byte-compatibility contract that lets you take over a Kamal-managed server without cleanup.

Newsletter

Release notes and new posts, once a month. No spam.

Prefer RSS? Subscribe to the feed →