ColdFusion MX Forms and Database Queries
Build database-backed forms with explicit validation, parameterized SQL, safe output, and predictable request flow.
A Plain HTML Form
<form method="post" action="save-customer.cfm">
<label for="name">Name</label>
<input id="name" name="name" type="text" maxlength="100">
<label for="email">Email</label>
<input id="email" name="email" type="text" maxlength="254">
<button type="submit">Save customer</button>
</form>
Period ColdFusion applications often used cfform and generated client-side validation. Plain HTML keeps the request visible and avoids depending on obsolete generated JavaScript.
Normalize and Validate Input
<cfparam name="form.name" default="">
<cfparam name="form.email" default="">
<cfset variables.cleanName = Trim(form.name)>
<cfset variables.cleanEmail = LCase(Trim(form.email))>
<cfset variables.errors = ArrayNew(1)>
<cfif Len(variables.cleanName) LT 1 OR Len(variables.cleanName) GT 100>
<cfset ArrayAppend(variables.errors, "Name must be between 1 and 100 characters.")>
</cfif>
<cfif Len(variables.cleanEmail) LT 3 OR Len(variables.cleanEmail) GT 254
OR NOT Find("@", variables.cleanEmail)>
<cfset ArrayAppend(variables.errors, "Enter a valid email address.")>
</cfif>
MX 7 includes additional validation functions, but explicit checks remain useful when documenting exact business rules.
Insert with cfqueryparam
<cfif ArrayLen(variables.errors) EQ 0>
<cfquery name="insertCustomer" datasource="support_portal">
INSERT INTO customers (name, email, status, created_at)
VALUES (
<cfqueryparam value="#variables.cleanName#"
cfsqltype="cf_sql_varchar" maxlength="100">,
<cfqueryparam value="#variables.cleanEmail#"
cfsqltype="cf_sql_varchar" maxlength="254">,
<cfqueryparam value="active"
cfsqltype="cf_sql_varchar" maxlength="20">,
CURRENT_TIMESTAMP
)
</cfquery>
<cflocation url="customers.cfm?saved=1" addtoken="false">
</cfif>
cfqueryparam provides type checking and bind parameters. It should be used for dynamic values, not only values you believe are dangerous.Query and Display
<cfquery name="customers" datasource="support_portal">
SELECT id, name, email, status
FROM customers
WHERE status = <cfqueryparam value="active" cfsqltype="cf_sql_varchar">
ORDER BY name
</cfquery>
<cfoutput query="customers">
<p>
<a href="customer.cfm?id=#URLEncodedFormat(id)#">
#HTMLEditFormat(name)#
</a><br>
#HTMLEditFormat(email)#
</p>
</cfoutput>
Editing a Record
<cfparam name="url.id" default="0">
<cfset variables.customerId = Val(url.id)>
<cfquery name="customer" datasource="support_portal" maxrows="1">
SELECT id, name, email, status
FROM customers
WHERE id = <cfqueryparam value="#variables.customerId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfif customer.recordCount EQ 0>
<cfheader statuscode="404" statustext="Not Found">
<cfoutput>Customer not found.</cfoutput>
<cfabort>
</cfif>
Post/Redirect/Get
After a successful insert or update, redirect to a normal GET page. This prevents an accidental form resubmission when the user refreshes the browser.
CSRF Protection in a Legacy Application
MX-era applications generally need a custom anti-CSRF token implementation. Generate a random value, store it in the session, place it in a hidden field, and compare it on POST.
<!--- Form page --->
<cflock scope="session" type="exclusive" timeout="5">
<cfset session.formToken = Hash(CreateUUID() & Now() & RandRange(100000, 999999))>
</cflock>
<cfoutput>
<form method="post" action="save-customer.cfm">
<input type="hidden" name="formToken" value="#HTMLEditFormat(session.formToken)#">
<!--- fields --->
</form>
</cfoutput>
<!--- Processing page --->
<cfparam name="form.formToken" default="">
<cfset variables.tokenIsValid = false>
<cflock scope="session" type="readonly" timeout="5">
<cfif StructKeyExists(session, "formToken")
AND Compare(form.formToken, session.formToken) EQ 0>
<cfset variables.tokenIsValid = true>
</cfif>
</cflock>
<cfif NOT variables.tokenIsValid>
<cfthrow type="Security.CSRF" message="Invalid form token.">
</cfif>
A modern platform may offer stronger built-in primitives. This example is a learning pattern for maintaining legacy code, not a claim that MX-era random-number or hash defaults meet modern cryptographic requirements.
Displaying Validation Errors
<cfif ArrayLen(variables.errors) GT 0>
<div class="error-summary">
<h2>Correct the following:</h2>
<ul>
<cfloop index="i" from="1" to="#ArrayLen(variables.errors)#">
<cfoutput><li>#HTMLEditFormat(variables.errors[i])#</li></cfoutput>
</cfloop>
</ul>
</div>
</cfif>