The previous lesson covered REST concepts generally. This one builds a real one: the CFC attributes that turn a component into a REST resource, registering it so ColdFusion actually knows it exists, and a complete CRUD example.
Learning Objectives
After completing this lesson, you'll be able to:
- Turn a CFC into a REST resource with rest and restPath.
- Expose functions as endpoints with httpMethod, produces, and consumes.
- Accept a path parameter with restArgSource.
- Register a REST service with restInitApplication, and know the real Lucee-vs-Adobe difference in that call.
How a Request Actually Reaches Your Code
CFC With rest="true"
restInitApplication() Registers It
/rest/[mapping]/[restPath]
Matching Function Runs
A Basic REST Component
component rest="true" restPath="products" {
remote array function getAllProducts() httpMethod="GET" produces="application/json" {
return [
{ id: 1, name: "Laptop", price: 999.99 },
{ id: 2, name: "Smartphone", price: 499.99 }
];
}
}cfcomponent's REST Attributes
| Attribute | Meaning |
|---|---|
| rest | Marks the component as REST-enabled (true/false) |
| restPath | The base path this component's resources are exposed under, case-sensitive |
cffunction's REST Attributes
| Attribute | Meaning |
|---|---|
| access | Must be remote to expose the function; as of ColdFusion 2018, it can be omitted entirely on a REST-enabled component |
| httpMethod | get, post, put, delete, head, or options; defaults to GET |
| restPath | A function-level sub-path, combined with the component's own restPath |
| produces | Comma-delimited MIME types this function can return; defaults to */* |
| consumes | Comma-delimited MIME types this function accepts from the client; defaults to */* |
produces and consumes each override the component-level setting when specified on an individual function.
A Real Example: A Path Parameter
remote struct function getProductById(required numeric id restArgSource="Path")
httpMethod="GET"
restPath="{id}"
produces="application/json" {
return { id: arguments.id, name: "Sample Product", price: 0.00 };
}restArgSource="Path" (Path, Form, Query, or Header) tells ColdFusion where to pull the value from, matched against the argument's own name, id in this case, against the {id} placeholder in restPath.
Registering the Service: restInitApplication
component {
this.name = "MyRestAPIApp";
public boolean function onApplicationStart() {
restInitApplication(expandPath("./api"), "api_v1");
return true;
}
}Every CFC in the given folder (and subfolders) that's REST-enabled gets registered under the service mapping name, api_v1 here. If you change a CFC's methods later, you need to re-run restInitApplication or click Refresh in the ColdFusion Administrator for the change to take effect.
A Real Engine Difference: restInitApplication's Signature
| Aspect | Adobe ColdFusion | Lucee |
|---|---|---|
| Parameters | dirPath, serviceMapping | dirPath, serviceMapping, default, password |
| Password requirement | Not required | Requires the web admin password as a parameter |
Code written for Adobe ColdFusion's simpler two-argument call needs the admin password added when porting to Lucee, it isn't optional there.
The URL Structure
http://server/rest/[serviceMapping]/[restPath] — the /rest/ segment is ColdFusion's fixed servlet mapping, api_v1 (or whatever you chose) is the serviceMapping from restInitApplication, and products (or {id}) comes from the component and function-level restPath values.
A Real Example: A Complete CRUD Component
component rest="true" restPath="products" {
remote array function getAllProducts() httpMethod="GET" produces="application/json" {
return productService.getAll();
}
remote struct function getProductById(required numeric id restArgSource="Path")
httpMethod="GET" restPath="{id}" produces="application/json" {
return productService.getById(arguments.id);
}
remote struct function createProduct(required string name restArgSource="Form", required numeric price restArgSource="Form")
httpMethod="POST" produces="application/json" {
return productService.create(arguments.name, arguments.price);
}
remote struct function updateProduct(required numeric id restArgSource="Path", required string name restArgSource="Form")
httpMethod="PUT" restPath="{id}" produces="application/json" {
return productService.update(arguments.id, arguments.name);
}
remote void function deleteProduct(required numeric id restArgSource="Path")
httpMethod="DELETE" restPath="{id}" {
productService.delete(arguments.id);
}
}productService here stands in for your actual data-access logic (a separate CFC, a query, whatever the real implementation is), the point of this example is the REST attribute wiring, not the business logic behind it.
A Real Feature Worth Knowing: PATCH Support
As of the ColdFusion 2018 release, httpMethod="PATCH" is supported for partial updates, but it requires a corresponding GET resource to already exist at the same path.
Beyond Native CFCs: Taffy and ColdBox
Native rest="true" CFCs work well for a straightforward API, but larger production APIs often reach for a dedicated framework instead: Taffy is a long-standing, lightweight REST framework built specifically for CFML, and ColdBox (with its Relax module) is the option if you're already using ColdBox's full MVC framework, covered later in this course. Both add built-in routing, authentication filtering, and custom status code handling that native CFCs leave you to build yourself.
Common Beginner Mistakes
Changing a REST CFC and expecting the change to appear immediately
ColdFusion scanned and registered the CFC when restInitApplication last ran. A code change needs restInitApplication re-run, or a manual Refresh in the ColdFusion Administrator, to actually take effect.
Assuming access="remote" is always required
As of ColdFusion 2018, it can be omitted entirely on a REST-enabled component's functions. Older code (or Lucee) may still need it set explicitly.
Mismatching the path placeholder and the argument name
restPath="{id}" has to match the actual argument name (id) that restArgSource="Path" is pulling from, a typo between them silently fails to bind.
Porting Adobe's two-argument restInitApplication call to Lucee unchanged
Lucee's version also requires the web admin password as a parameter, code written for Adobe's simpler signature needs that added.
Best Practices
- Keep REST-enabled CFCs organized in a dedicated folder, matching what restInitApplication scans.
- Set produces/consumes explicitly when content negotiation actually matters, rather than relying on the */* default everywhere.
- Use restArgSource="Path" for identifiers and Form (or a JSON body) for created/updated payloads, matching how the data actually arrives.
- Reach for Taffy or ColdBox/Relax once a native-CFC API's routing and auth needs start outgrowing what rest="true" alone comfortably handles.
Interview Questions
What two attributes turn a CFC into a REST resource?
rest="true" and restPath on the cfcomponent tag (or component syntax).
What does restArgSource control, and what are its possible values?
Where a function argument's value comes from: Path, Form, Query, or Header, matched against the argument's own name.
Why might a code change to a REST CFC not show up when you call the endpoint?
The service was registered by restInitApplication at an earlier point. Re-running it, or clicking Refresh in the ColdFusion Administrator, is needed for the change to actually take effect.
What's a real, concrete difference between Adobe's and Lucee's restInitApplication?
Lucee's version requires the web admin password as a parameter; Adobe's simpler two-argument form (dirPath, serviceMapping) doesn't need one at all.
Summary
In this lesson, you turned a CFC into a REST resource, exposed functions with httpMethod/produces/consumes, accepted a path parameter with restArgSource, registered the service with restInitApplication, built a complete CRUD example, and covered a real Lucee-vs-Adobe difference in that registration call.
What's Next?
The next lesson covers consuming APIs, calling an external REST service from ColdFusion with cfhttp.