Command Line Tools
wheels generate api-resource
wheels generate api-resource
Section titled “wheels generate api-resource”Generate a complete RESTful API controller with advanced features like pagination, filtering, sorting, and authentication.
Synopsis
Section titled “Synopsis”wheels generate api-resource [name] [options]wheels g api-resource [name] [options]Description
Section titled “Description”The wheels generate api-resource command creates a production-ready RESTful API controller optimized for JSON APIs. It generates API-specific controllers with no view rendering logic, including optional features like pagination, filtering, sorting, authentication, and API versioning.
The generated controllers use provides("json") and renderWith() to return JSON responses with proper HTTP status codes for REST operations.
Arguments
Section titled “Arguments”| Argument | Description | Default |
|---|---|---|
name | Resource name (singular or plural) | Required |
Options
Section titled “Options”| Option | Description | Default |
|---|---|---|
--version | API version (v1, v2, etc.) | v1 |
--format | Response format (json, xml) | json |
--auth | Include authentication | false |
--pagination | Include pagination | true |
--filtering | Include filtering | true |
--sorting | Include sorting | true |
--skipModel | Skip model generation | false |
--skipMigration | Skip migration generation | false |
--skipTests | Skip test generation | false |
--namespace | API namespace | api |
--docs | Generate API documentation template | false |
--force | Overwrite existing files | false |
CommandBox Parameter Syntax
Section titled “CommandBox Parameter Syntax”This command supports multiple parameter formats:
- Named parameters:
name=value(e.g.,name=products,version="v2") - Flag parameters:
--flagequalsflag=true(e.g.,--authequalsauth=true) - Flag with value:
--flag=valueequalsflag=value(e.g.,--version=v2)
Parameter Mixing Rules:
ALLOWED:
- All positional:
wheels generate api-resource products - All named:
name=products version="v2" auth=true - Positional + flags:
wheels generate api-resource products --auth --skipModel
NOT ALLOWED:
- Positional + named:
wheels generate api-resource products version="v2"(causes error)
Recommendation: Use positional for name + flags for options: wheels generate api-resource products --auth --version=v2
Examples
Section titled “Examples”Basic API Controller
Section titled “Basic API Controller”Generate a simple API controller:
# Positional name onlywheels generate api-resource productsCreates:
/models/Product.cfc- Model file/controllers/api/v1/Products.cfc- Versioned API controller with pagination, filtering, sorting
Without Advanced Features
Section titled “Without Advanced Features”Generate a minimal API controller without optional features:
# Using named parameters (all named)wheels g api-resource name=products pagination=false filtering=false sorting=false
# OR using flags (positional + flags) - RECOMMENDEDwheels g api-resource products --pagination=false --filtering=false --sorting=falseCreates a simple controller with only basic CRUD operations.
With Authentication
Section titled “With Authentication”Generate API controller with authentication:
# Using flagwheels generate api-resource products --authIncludes Bearer token authentication that requires Authorization header for create, update, delete actions.
Custom Version and Namespace
Section titled “Custom Version and Namespace”Generate API controller with custom versioning:
# Using flags with valueswheels generate api-resource products --version=v2 --namespace=publicCreates /controllers/public/v2/Products.cfc
Skip Model Generation
Section titled “Skip Model Generation”Generate only the controller (model already exists):
# Using flagwheels generate api-resource products --skipModelComplete Setup with Documentation
Section titled “Complete Setup with Documentation”Generate everything with API documentation:
# Multiple flagswheels generate api-resource products --auth --docsCreates:
/models/Product.cfc/controllers/api/v1/Products.cfcwith authentication/app/docs/api/products.md- API documentation
Generated Controller Features
Section titled “Generated Controller Features”With All Features Enabled
Section titled “With All Features Enabled”wheels generate api-resource products --auth --pagination --filtering --sortingGenerates:
component extends="wheels.Controller" {
function config() { provides("json"); filters(through="setJsonResponse"); filters(through="authenticate", except="index,show"); }
/** * GET /products * Supports: ?page=1&perPage=25&sort=name,-price&filter[name]=widget */ function index() { local.page = params.page ?: 1; local.perPage = params.perPage ?: 25; local.options = {}; local.options.page = local.page; local.options.perPage = local.perPage;
if (structKeyExists(params, "sort")) { local.options.order = parseSort(params.sort); }
if (structKeyExists(params, "filter")) { local.options.where = parseFilter(params.filter); }
local.products = model("Product").findAll(argumentCollection=local.options);
local.response = { data = local.products, meta = { pagination = { page = local.products.currentPage ?: local.page, perPage = local.perPage, total = local.products.totalRecords ?: 0, pages = local.products.totalPages ?: 1 } } };
renderWith(data=local.response); }
function show() { /* ... */ } function create() { /* ... */ } function update() { /* ... */ } function delete() { /* ... */ }
// Helper methods for pagination, filtering, sorting, auth private function authenticate() { /* ... */ } private function parseSort(required string sort) { /* ... */ } private function parseFilter(required struct filter) { /* ... */ }}Adding Routes
Section titled “Adding Routes”After generating your API resource, add routes to /config/routes.cfm:
Default Namespaced Routes
Section titled “Default Namespaced Routes”// Add inside mapper() blocknamespace(name="api", function() { namespace(name="v1", function() { resources(name="products", except="new,edit"); });});Creates routes:
GET /api/v1/productsGET /api/v1/products/:keyPOST /api/v1/productsPUT /api/v1/products/:keyDELETE /api/v1/products/:key
Custom Version
Section titled “Custom Version”namespace(name="api", function() { namespace(name="v2", function() { resources(name="products", except="new,edit"); });});No Namespace
Section titled “No Namespace”If you used --namespace="":
resources(name="products", except="new,edit");Feature Details
Section titled “Feature Details”Pagination
Section titled “Pagination”When --pagination is enabled (default):
Request:
curl "http://localhost:8080/api/v1/products?page=2&perPage=10"Response:
{ "data": [ /* products */ ], "meta": { "pagination": { "page": 2, "perPage": 10, "total": 100, "pages": 10 } }}Filtering
Section titled “Filtering”When --filtering is enabled (default):
Request:
curl "http://localhost:8080/api/v1/products?filter[name]=widget&filter[minPrice]=10"The generated controller includes a parseFilter() method with TODO comments for you to implement your filtering logic.
Sorting
Section titled “Sorting”When --sorting is enabled (default):
Request:
# Sort by name ascending, then price descendingcurl "http://localhost:8080/api/v1/products?sort=name,-price"The - prefix indicates descending order.
Authentication
Section titled “Authentication”When --auth is enabled:
Request:
curl -X POST http://localhost:8080/api/v1/products \ -H "Authorization: Bearer YOUR_TOKEN_HERE" \ -H "Content-Type: application/json" \ -d '{"product":{"name":"Widget"}}'The generated controller includes authentication methods that you need to implement with your actual token validation logic.
HTTP Status Codes
Section titled “HTTP Status Codes”The generated controller uses proper REST HTTP status codes:
| Action | Success Status | Error Status |
|---|---|---|
index | 200 OK | - |
show | 200 OK | 404 Not Found |
create | 201 Created | 422 Unprocessable Entity |
update | 200 OK | 404 Not Found, 422 Unprocessable Entity |
delete | 204 No Content | 404 Not Found |
auth failure | - | 401 Unauthorized |
Testing Your API
Section titled “Testing Your API”Basic Requests
Section titled “Basic Requests”# List products with paginationcurl "http://localhost:8080/api/v1/products?page=1&perPage=25"
# Get specific productcurl http://localhost:8080/api/v1/products/1
# Create productcurl -X POST http://localhost:8080/api/v1/products \ -H "Content-Type: application/json" \ -d '{"product":{"name":"Widget","price":29.99}}'
# Update productcurl -X PUT http://localhost:8080/api/v1/products/1 \ -H "Content-Type: application/json" \ -d '{"product":{"price":39.99}}'
# Delete productcurl -X DELETE http://localhost:8080/api/v1/products/1With Filtering and Sorting
Section titled “With Filtering and Sorting”# Filter and sortcurl "http://localhost:8080/api/v1/products?filter[name]=widget&sort=-createdAt"
# Pagination with filterscurl "http://localhost:8080/api/v1/products?page=1&perPage=10&filter[minPrice]=20&sort=name"With Authentication
Section titled “With Authentication”# With Bearer tokencurl -X POST http://localhost:8080/api/v1/products \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"product":{"name":"Widget"}}'Example Responses
Section titled “Example Responses”Success with Pagination (200 OK):
{ "data": [ { "id": 1, "name": "Widget", "price": 29.99, "createdAt": "2023-01-01T12:00:00Z", "updatedAt": "2023-01-01T12:00:00Z" } ], "meta": { "pagination": { "page": 1, "perPage": 25, "total": 1, "pages": 1 } }}Validation Error (422):
{ "error": "Validation failed", "errors": [ { "property": "name", "message": "This field is required" } ]}Not Found (404):
{ "error": "Record not found"}Unauthorized (401):
{ "error": "Unauthorized"}Customization
Section titled “Customization”Implementing Filter Logic
Section titled “Implementing Filter Logic”Edit the generated parseFilter() method:
private function parseFilter(required struct filter) { local.where = []; local.params = {};
if (structKeyExists(arguments.filter, "name")) { arrayAppend(local.where, "name LIKE :name"); local.params.name = "%#arguments.filter.name#%"; }
if (structKeyExists(arguments.filter, "minPrice")) { arrayAppend(local.where, "price >= :minPrice"); local.params.minPrice = arguments.filter.minPrice; }
if (structKeyExists(arguments.filter, "category")) { arrayAppend(local.where, "category = :category"); local.params.category = arguments.filter.category; }
return arrayLen(local.where) ? arrayToList(local.where, " AND ") : "";}Implementing Authentication
Section titled “Implementing Authentication”Edit the generated isValidToken() method:
private function isValidToken(required string token) { // Example: Check against database local.apiKey = model("ApiKey").findOne(where="token = :token", token=arguments.token);
if (isObject(local.apiKey) && local.apiKey.active) { // Store user context in session/request request.user = local.apiKey.user(); return true; }
return false;}Adding More Sortable Fields
Section titled “Adding More Sortable Fields”Edit the parseSort() method:
private function parseSort(required string sort) { local.allowedFields = ["id", "name", "price", "category", "createdAt", "updatedAt"]; // ... rest of method}Best Practices
Section titled “Best Practices”- Use Versioning: Always version your APIs (
--version=v1) - Enable Pagination: Prevent performance issues with large datasets
- Add Authentication: Secure your API endpoints with
--auth - Document Your API: Use
--docsflag and keep documentation updated - Implement Filtering: Customize
parseFilter()for your model fields - Whitelist Sort Fields: Only allow sorting on indexed fields
- Use Proper Status Codes: 201 for creation, 204 for deletion
- Return Error Details: Always include error messages for 4xx/5xx
- Rate Limiting: Consider adding rate limiting for public APIs
- CORS Headers: Add CORS support for browser-based clients
Comparison with Other Commands
Section titled “Comparison with Other Commands”| Feature | api-resource | controller --api | scaffold |
|---|---|---|---|
| Generates model | Optional | No | Yes |
| Generates views | No | No | Yes |
| Actions | REST only | REST only | Full CRUD |
| Format | Configurable | JSON only | HTML + JSON |
| Versioning | Yes | No | No |
| Pagination | Optional | No | No |
| Filtering | Optional | No | No |
| Sorting | Optional | No | No |
| Authentication | Optional | No | No |
| Best for | Production APIs | Simple APIs | Full-stack apps |
See Also
Section titled “See Also”- wheels generate controller - Generate standard controllers
- wheels generate model - Generate models
- wheels generate scaffold - Generate full CRUD resources
- Wheels REST Documentation - REST API best practices