help  namespaces  categories  help  rss feeds

Projects:Relax

<< Back to Dashboard | << Projects Viewer

Contents

ColdBox Relax: ReSTful Tools For Lazy Experts

What is Relax? ColdBox Relax is a set of ReSTful tools for lazy experts. We pride ourselves in helping you (the developer) work smarter and ColdBox Relax is a tool to help you document your projects faster. ColdBox Relax provides you with the necessary tools to automagically model, document and test your ReSTful services. One can think of ColdBox Relax as a way to describe ReSTful web services, test ReSTful web services, monitor ReSTful web services and document ReSTful web services–all while you relax!

Requirements

  • ColdBox Relax is a ColdBox 3 Module and can be installed in any ColdBox 3 application.
  • ColdFusion 8 and above.
  • MySQL, MSSQL, Oracle or PostgresSQL for Log Viewing Capabilities (Optional).

Installation

Download the ColdBox Relax module and drop into your ColdBox modules folder. Start your application and visit the relax entry point:

http://myapp.com/index.cfm/relax/home

Capabilities

So what can you do with Relax?

  • Define your ReSTful services via our awesome Relax DSL (Programmatic or Implicit)
  • Generate ColdBox Routes from described ReSTful routes
  • Using this DSL we will document your entire ReSTful web service and you will be able to export the documentation in the following formats:
    • HTML
    • PDF
    • Trac Wiki Markup
    • Mediawiki Wiki Markup (CodexWiki)
  • Using the DSL you will also have access to our RelaxURL (pronounced "Relaxer") tool, from which you can test your ReSTful web services
  • Use the RelaxURL tool to test against ANY ReSTful service
  • Keep a history of your latest Relaxed test requests so you can rebuild your request and test again
  • LogBox integration so you can use the RelaxLogs viewer to monitor and see your log db entries
  • Programmatic DSL for configuration of your RESTful services

Videos

Relax Intro

Version 1.5 Features

Version 1.4 Features

How to start?

To start using Relax you should start by configuring the Relax module for operation, then create our Relax DSL for our ReSTful web services. Finally, we can move into the RelaxLogs setup so we can monitor the logs produced by our RESTful service.

Relax Module Configuration

Let's open the module's configuration file ModuleConfig.cfc and see what we can modify in it. Look for the settings structure, this is the configuration structure for the Relax module and you have some control over it. Below is the default configuration structure:

// Relax Configuration Settings
settings = {
	// Relax Version: DO NOT ALTER
	version = this.version,
	// The location of the relaxed APIs, in instantiation path
	apiLocationPath = "#moduleMapping#.resources",
	// Default API to load
	defaultAPI = "myapi",
	// History stack size, the number of history items to track in the RelaxURL
	maxHistory = 10,
	// logbox integration information needed for log viewer to work
	// this means that it can read tables that are written using the logbox's DB Appender.
	relaxLogs = {
		// THE CF DATASOURCE NAME
		datasource = "relax",
		// THE DB TO USE FOR LOGS, AVAILABLE ADAPTERS ARE: mysql, mssql, postgres, oracle
		adapter = "mysql",
		// THE TABLE WHERE THE LOGS ARE
		table 	= "api_logs",
		// PAGING MAX ROWS
		maxRows = 50,
		// PAGING CARROUSEL BANDGAP
		bandGap = 3
	}
};

Relax Module Settings

And here are the settings that you can modify:

Setting Type Required Default Description
apiLocationPath instantiation path true #moduleMapping#.resources The instantiation path prefix to the location where all your Relaxed services are defined under. By default it looks at the resources folder in the module.
defaultAPI folderName true myapi The name of the folder that holds the default API to load when Relax loads. By default it points to our sample api declaration.
maxHistory numeric true 10 The default number of history items to keep on a stack when making requests via the RelaxURL.
relaxLogs struct true --- The structure used to configure the RelaxLogs log viewer integration.

RelaxLogs Settings

RelaxLogs structure settings:

Setting Type Required Default Description
datasource string true --- The datasource registered in the ColdFusion administrator to use for connectivity
table string true --- The database table where all the log entries are located in.
adapter string true --- The adapter to use for talking to the correct database implementation. The available adapters are:
  • mysql
  • mssql
  • postgres
  • oracle
maxrows numeric true 50 The maximum number of rows to retrieve in the RelaxLogs as paging is provided.
bandGap numeric true 3 The number of pages to display left and right of the paging carrousel when paging is provided.

Note: To use the RelaxLogs you will need to make some configuration adjustments. Essentially, you need to configure RelaxLogs to work with a database table with columns specified by the LogBox Database Appender.

Relax DSL

Once you have configured the Relax module setup and ready for operation you can start by defining your RESTful service. You will do this by creating a Simple CFC–usually in the root of the resource folder, for simplicity–named Relax.cfc or Relax.json. In this component or JSON file, you will create several public variables (using the this scope) that will define your service's parameters, headers, resources and so much more. You can also use our programmatic DSL, which is much cleaner than implicit data structures. This CFC will represent your RESTful service and ColdBox Relax will generate documentation from it, create testable URLs for you and it is a great way for you to model your service. Please note that if you use JSON for defining your service, it must validate according to the same structure elements you will see here. http://www.jsonlint.com is a great way for you to format and validate your JSON representations.

The implicit data structure definitions could be deprecated in future versions in favor of the programmatic DSL.

Variable Type Description
relax struct The structure used to define the ReSTful service
globalHeaders array of structs Where you define global headers your ReSTful service expects
globalParameters array of structs Where you define global parameters your ReSTful service expects
resources array of structs Where you define your ReSTful resource patterns.
component{

	// relax dsl
	this.relax = {};
	
	// Global Headers
	this.globalHeaders = [];
	
	// Global Parameteres
	this.globalParameters = [];
	
	// Resources
	this.resources = [];
}

relax configuration

The following properties are used to understand your ReSTful service:

Variable Type Description
title string The title for your ReSTful service. Used for documentation purposes
description html or string A string or HTML description of your service. This can be whatever you like.
entryPoint URL or struct of URLs The main URL entry point for your ReSTful service. e.g. http://api.coldbox.org or if your API service has different URLs or tiers you can make this key be a named structure.
extensionDetection boolean A flag that denotes if this ColdBox ReSTful service can detect the format extension via the resource URL. By default ColdBox detects an extension in any resource request. Used for documentation and routes generation. e.g. http://api.coldbox.org/my/resource.json, http://api.coldbox.org/my/resource.xml
validExtensions list A list of extensions this ReSTful service can transform to. Used for documentation and routes generation.
throwOnInvalidExtension boolean A flag that denotes if the ReSTful service will throw a 406 error when an invalid extension is used or to ignore the error.

Here is a sample of a single entry point:

// This is where we define our ReSTful service, this is usually
// our first place before even building it, we spec it out.
this.relax = {
	// Service Title
	title = "My ReSTful Service",
	// Service Description
	description = "A very cool ReSTful Service",
	// Service entry point
	entryPoint = "http://www.myapi.com",
	// Does it have extension detection via ColdBox
	extensionDetection = true,
	// Valid format extensions
	validExtensions = "xml,json,jsont,wddx",
	// Does it throw exceptions when invalid extensions are detected
	throwOnInvalidExtension = false		
};

Here is a sample of a multiple entry point service:

// This is where we define our ReSTful service, this is usually
// our first place before even building it, we spec it out.
this.relax = {
	// Service Title
	title = "My ReSTful Service",
	// Service Description
	description = "A very cool ReSTful Service",
	// Service entry point
	entryPoint = {
		dev =  "http://dev.myapi.com",
		staging =  "http://stg.myapi.com",
		production =  "http://www.myapi.com"
	},
	// Does it have extension detection via ColdBox
	extensionDetection = true,
	// Valid format extensions
	validExtensions = "xml,json,jsont,wddx",
	// Does it throw exceptions when invalid extensions are detected
	throwOnInvalidExtension = false		
};

globalHeaders configuration

This section is optional and only used if you would like to describe the HTTP headers that your service will listen to in a global manner. For example, if you require a X-Auth-Token to ensure every request has been authenticated you would want to take advantage of this feature. As with the variables defined before these are an array of structures with the following keys:

Variable Type Description
name string The name of the HTTP Header
description html or string A string or HTML description of your HTTP header. This can be whatever you like.
type string The type of the HTTP header, usually it could be string, boolean, numeric, binary, etc.
required Boolean Whether the header is required for any calls or not. The RelaxURL console will use this value if used.
default any A default value the header is associated with if not passed or sent to the service.

Here is a sample:

// Global API Headers
this.globalHeaders = [
	// Sample global header, Available keys: name,description,required,default,type
	{name="apikey",description="The apikey needed for request authentication.",required=true}
];

globalParameters configuration

This section is optional and only used if you would like to describe the HTTP parameters that your service will listen to in a global manner. For example, if you require a security token to ensure every request has been authenticated, or a format variable you would want to take advantage of this feature. As with the variables defined before these are an array of structures with the following keys:

Variable Type Description
name string The name of the HTTP Parameter
description html or string A string or HTML description of your HTTP Parameter. This can be whatever you like.
type string The type of the HTTP Parameter, usually it could be string, boolean, numeric, binary, etc.
required Boolean Whether the Parameter is required for any calls or not. The RelaxURL console will use this value if used.
default any A default value the Parameter is associated with if not passed or sent to the service.

Here is a sample:

// Global API Parameters
this.globalParameters = [
	// Sample global parameter, Available keys: name,description,required,default,type
	{name="apikey",description="The apikey needed for request authentication.",required=true},
	{name="format",description="An optional format variable used for content negotiation",required=false, default="json"}
];

resources configuration

The following section is essential as it describes the ReSTful resources you will expose through your service. Please remember that order is important as it will be used to generate the ColdBox routes file, which in turn are your ReSTful routes. We will create a array of structures with the following keys for each resource:

Variable Type Description
pattern string The ColdBox route pattern to define, you can use ALL the same parameters you do in a ColdBox URL Mapping route.
handler string The event notation for the package+event to redirect this request to. This key is optional as the handler can be embedded also in the pattern as :handler.
action string or JSON or Implicit Struct The normal action or ReSTful action (json) to redirect this request to. This key is optional as the action can be embedded also in the pattern as :action or left blank to default it to index. ReSTful routing can also be used, which is what we recommend, by making this entry a JSON structure: e.g. action="{'get':'list', 'post':'create', 'delete':'remove'}"
description HTML or string A description about the resource you are exposing. Try to be descriptive, use HTML or make it pretty.
methods list A list of allowed HTTP methods this resource can listen to.
defaultMethod string The default method this resource responds to. By default it is set to GET.
defaultFormat string The default format this resource responds to. By default format is empty.
headers array of headers AAn array of structures where you define HTTP headers this resource can listen to. The header structure to use is the same as the global headers described before: name, description, type, required and default.
parameters array of parameters AAn array of structures where you define HTTP URL/FORM parameters this resource can listen to. The structure to use is the same as the global headers described before: name, description, type, required and default.
placeholders array of placeholders AAn array of structures where you describe the pattern placeholders (:placeholder). These placeholders get translated by ColdBox URL mappings into request collection variables, so you can use this array to define them for documentation purposes. The structure to use is the same as the global headers described before: name, description, type, required and default.
response structure A structure that can hold two keys: schemas and samples for defining them for the resource.

Here is a sample:

// Define our Relaxed ReSTful resources in order just like you are defining routes
// Each header, parameter or placeholder is a structure with the following keys:
// {name="",type="",required="",default="",description=""}
this.resources = [
	{pattern="/api/users",handler="rest.user",action="list", defaultMethod="GET", defaultFormat="json"
	 description="Returns all users",
	 methods="GET",headers=[],parameters=[],placeholders=[]},
	 
	 {pattern="/api/user/:username",handler="rest.user",action="{'get':'view','post':'create','put':'update','delete','remove'}",
	  description="The representation for system users.  You can also interact with creation, updating and deletion via this resource",
	  methods="GET,POST,PUT,DELETE",
	  headers=[],
	  parameters=[
		{name="firstName",description="The user firstname. Only used on PUT and POST operations",required="false"},
		{name="lastName",description="The user lastname. Only used on PUT and POST operations",required="false"},
		{name="email",description="The user email. Only used on PUT and POST operations",required="false"}
	  ],
	  placeholders=[{name="username",description="The resource username to interact with",required=true}]}
];

That's it! Once your DSL is defined, you can start up your relax module and get cooking with it. Here are some screenshots and videos about Relax.

Schemas & Samples

You can define samples and schemas for your resources by using the response structure by using the schemas and samples keys, which in turn are array of definitions.

response={
	schemas=[
		{format="json", description="The following will be returned when the format requested is JSON.", body=fileRead("#dirPath#schemas/user/user.json") },
		{format="xml", description="The following will be returned when the format requested is XML.", body=fileRead("#dirPath#schemas/user/user.xsd") }
	],
	samples=[
		{format="json", description="The basic user information will be returned in a flat object.", body=fileRead("#dirPath#samples/user/user.json")},
		{format="json", description="If the requested user is not found, or some other error has occurred, the resopnse will be like the following.", body=fileRead("#dirPath#samples/user/failure.json")}
	]
}

As you can see, you can define multiple declarations for each category. Each containing:

Variable Type Description
format string The format of the schema or sample
description string The description of the schema or sample
action string The contents of the schema or sample, usually in the specified format like JSON or XML.

Programmatic DSL

A part from the implicit configuration, you can use our more versatile and clean programmatic DSL to configure your relaxed resources. The Relax DSL configuration object which is mixed into a simple Relax CFC Definition Object so you can use all the methods in this CFC to define RESTful web services. All functions can be concatenated to create a nice programmatic DSL.

service()

The relaxed service information. From here you will define the RESTful service endpoints, extension detection, valid formats, and more.

Arguments

Argument Type Required Default Description
title any Yes --- The title of the RESTful service
description any Yes --- The description of the RESTful service
entryPoint any Yes --- A simple URL or a structure of entry points
extensionDetection any No true Will this API do extension detection
validExtensions any No json,jsont,xml,html,htm,rss The valid extensions to detect
throwOnInvalidExtensions any No false Throw on invalid extensions or not

defaultMethod()

Define a default HTTP method for a resource

Arguments

Argument Type Required Default Description
method any Yes --- The HTTP method that will be the default

placeholder()

Add a placeholder to a resource

Arguments

Argument Type Required Default Description
name any Yes --- The name of the placeholder
description any No The description of the placeholder
required any No false Is the placeholder required
default any No The default value of the placeholder
type any No string The type of the placeholder

schema()

Append a schema to a resource

Arguments

Argument Type Required Default Description
format any Yes --- The format of the schema
description any No The description of the schema
body any No The body contents of the schema

globalHeader()

Add a global header to the relax definition

Arguments

Argument Type Required Default Description
name any Yes --- The name of the header
description any No The description of the header
required any No false Is the header required or not
default any No The default value of this header
type any No string The type of the incoming header

resource()

Add a new resource to the API definition, you can then concatenate more methods to the same resource: methods(),description(),header(),parameter(),placeholder(),schema(),sample()

Arguments

Argument Type Required Default Description
pattern any Yes --- The SES pattern to register for the resource
handler any No --- The description of the parameter
action any No --- Is the parameter required or not

setPlaceHolders()

Set all the required placeholders of a resource

Arguments

Argument Type Required Default Description
placeholders array Yes --- Set the placeholders of a resource

sample()

Append a sample to a resource

Arguments

Argument Type Required Default Description
format any Yes --- The format of the sample
description any No The description of the sample
body any No The body contents of the sample

setParams()

Set all the required parameters of a resource

Arguments

Argument Type Required Default Description
params array Yes --- Set the parameters of a resource

globalParam()

Add a global parameter to the relax definition

Arguments

Argument Type Required Default Description
name any Yes --- The name of the parameter
description any No The description of the parameter
required any No false Is the parameter required or not
default any No The default value of this parameter
type any No string The type of the incoming parameter

methods()

Add methods to a resource

Arguments

Argument Type Required Default Description
methods any Yes --- The methods list to allow

description()

Add a description to a resource

Arguments

Argument Type Required Default Description
description any Yes --- The description

defaultFormat()

Define a default return format for a resource

Arguments

Argument Type Required Default Description
format any Yes --- The format that will be the default

setHeaders()

Set all the required headers of a resource

Arguments

Argument Type Required Default Description
headers array Yes --- Set the headers of a resource

header()

Add a header to a resource

Arguments

Argument Type Required Default Description
name any Yes --- The name of the header
description any No The description of the header
required any No false Is the header required
default any No The default value of the header
type any No string The type of the header

param()

Add a param to a resource

Arguments

Argument Type Required Default Description
name any Yes --- The name of the param
description any No The description of the param
required any No false Is the param required
default any No The default value of the param
type any No string The type of the param

Screenshots

RelaxLogs

The RelaxLogs section of the module is a nice log viewer that can tap to any table that was used or has the columns the LogBox Database Appender talks to. This is a great way to monitor your service log entries and report on them. So let's configure it:

LogBox Service Configuration

In order for our RelaxLogs to be able to communicate with your log tables you must enable logging in your application. This is done via LogBox–which is already in all ColdBox applications. To get things working you simply need to add a Appender (or destination) that will store the log entries in a database. If you are not building a ColdBox application–which you should–then have no worries because LogBox can work with any ColdFusion application or framework. Logging your ReSTful service in this manor is helpful as it allows you to maintain a history of your ReSTful service's status and operation. All production sites are recommended to follow these steps:

1. LogBox Setup

The following is the programmatic approach to adding the LogBox Database Appender so you can log entries to the database of your choice.

//LogBox DSL
logBox = {
    // Define Appenders
    appenders = {
        // A simple tracer appender
        coldboxTracer = { class="coldbox.system.logging.appenders.ColdboxTracerAppender" },
        // Database Appender Registration
        dbAppender = {
            class="coldbox.system.logging.appenders.DBAppender",
            properties = {
                // The datasource to connect to
                dsn = "relax", 
                // The table to log to
                table="api_logs", 
                // If the table does not exist, then create it
                autocreate=true, 
                // The type to use for long text inserting.
                textDBType="longtext"
            }
        }
    },
    // Root Logger
    root = { levelmax="INFO", appenders="coldboxTracer" }
};

Once you define the Appender in your configuration, LogBox can send messages to this destination. We suggest your review your LogBox skills before anything to understand how Loggers work and how you can even declare categories that point to specific destinations.

2. Custom LogBox Category

We suggest you create a special LogBox category and assign it this Database Appender destination. This will provide you with the ability that only log messages with this category will go to your database. We mostly do this to not overpopulate our DB with other kinds of log messages.

//LogBox DSL
logBox = {
    // Define Appenders
    appenders = {
        // A simple tracer appender
        coldboxTracer = { class="coldbox.system.logging.appenders.ColdboxTracerAppender" },
        // Database Appender Registration
        dbAppender = {
            class="coldbox.system.logging.appenders.DBAppender",
            properties = {
                // The datasource to connect to
                dsn = "relax", 
                // The table to log to
                table="api_logs", 
                // If the table does not exist, then create it
                autocreate=true, 
                // The type to use for long text inserting.
                textDBType="longtext"
            }
        }
    },
    // Root Logger
    root = { levelmax="INFO", appenders="coldboxTracer" },
     
    // Custom LogBox Category
    categories = {
        myRestService = { levelMax="DEBUG", appenders="dbAppender"}
    }
};

3. Logger Injection

Once this is done, we can inject our logger objects that LogBox provides into our handlers or domain objects that will send messages to our log tables. This is done via ColdBox's WireBox Dependency Injection engine and our Autowire DSL. The important part of this section is that we inject the logger with the category we setup in our configuration.

component name="Awesome REST Handler"{
 
    // inject correct logger
    property name="logger" inject="logbox:logger:myRestService";
     
     
    // log any call to any event in this awesome REST handler
    function preHandler(event,action){
        if( logger.canDebug() ){
            logger.debug("A request to relax/logs/help/ was made.", getHTTPRequestData() );
        }
    }
 
}

As you can see we first check if we can debug and then send a debug message to LogBox. The second argument of any Logger method can be any variable (simple or complex) that we would like to log. In our case, since it is a ReSTful service, we send the request data out for logging. Good for debugging.

4. Go Relax!

You did the hard work, now go relax! You can now configure the relaxLogs structure in the modules configuration (as shown before) and you are ready to roll.

 
Download in other Formats:
markup Markup | html HTML | word Word

comments Comments (7)

jlamoree@ecivis.com's Gravatar

Joseph Lamoree said

at 04:44:30 PM 25-Apr-2011

What's the best way to document structured data returned after a resource request? Regardless of format detection, the response will have a logical data structure that I'd like to document. It would be super awesome if I could add my JSON Schemas to the Relax DSL.
lmajano@gmail.com's Gravatar

Luis Majano said

at 06:25:35 PM 25-Apr-2011

Jason, can you expand on this?
jlamoree@ecivis.com's Gravatar

Joseph Lamoree said

at 06:19:07 PM 26-Apr-2011

For example, the response format of /v1/users/:username is like this: http://pastebin.com/qyWVhK7n And here are successful and unsuccessful response examples: http://pastebin.com/S6GtQFhn I'd like to be able to have that information in my Relax documentation somehow.
lmajano@gmail.com's Gravatar

Luis Majano said

at 07:16:57 PM 26-Apr-2011

Hmm very interesting, can you follow up on this conversation in our google group to get more ideas
jlamoree@ecivis.com's Gravatar

Joseph Lamoree said

at 01:39:40 PM 28-Apr-2011

tim@chair9design.com's Gravatar

Tim Brown said

at 04:27:55 PM 17-Sep-2013

Your relax module is awesome! One thing that I'm really wishing was configurable was the use of the word Relax throughout. You module really does make our lives easier, but that term doesn't really resonate with our customer if we were to let them peruse the documentation relax creates for our restful service. I would be awesome if there was a way to in the module config set a variable that replaced the relax portion in the urls with something like "docs". This would allow us to make a URL like below that we can send people to view our API documentation. http://mysite.com/someapi/docs [vs] http://mysite.com/someapi/relax This ability to customize this relax dashboard / paths whilst maintaining the ability to upgrade the module would really set this solution apart and allow for easier adoption in a corporate environment where branding on all products being shown to customers is as cohesive as possible. Thanks again for ColdBox!
lmajano@gmail.com's Gravatar

Luis Majano said

at 11:51:08 AM 19-Sep-2013

Tim, can you add an issue for this in our tracker.

ColdBox Books