RESTful Jaxer

Jaxer, the Ajax server, can do a lot more than "just" serve pages and handle callbacks (function calls from your browser back to Jaxer). With Jaxer 1.0, you can easily create a variety of web services: RESTful, RPC-based, and almost anything else you can imagine. Hooks are already in place for a generic web service and for automatic RPC-based services. Here I'll demonstrate a simple RESTful service based on the generic web service hook.

You'll need the latest Jaxer (currently release 1.0) and the local_jaxer/conf folder contents that ship with it. If you've upgraded from a previous version of Jaxer or Aptana Studio (which has Jaxer built in) take a look at the Guide page for instructions on updating your local_jaxer configuration to 1.0.

Here's a synopsis of all it takes to create a service (RESTful or otherwise):
  1. create a folder called "jaxer-service"
  2. put your code file in that folder (or anywhere under that folder if you need more hierarchy)
Your code is automatically loaded and executed whenever someone calls it. So now let's see how that works.

To offer a service through Jaxer, rather than a page, you'll need to put your code where the web server + Jaxer will recognize it as a service. By default, that means somewhere in your docroot where the path to it will contain "jaxer-service" or "jaxer-rpc". In a Studio project, create a folder with one of those names, and put your code in that folder or in a subfolder. For this example, I've created a very simple folder structure, one folder with two files:

This does two things: identifies the JavaScript code to Jaxer as a service, and prevents direct serving of your JavaScript file to a browser.

The service itself is just a JavaScript file that gets loaded (from disk or from cache) and executed every time the service is called. Your code has full access to the information in the request, and needs to set the response as you wish. Here I created a service that simply shows you much of the data available:

tests.js:

The service itself is done. (Of course you can now build an entire services layer, loading other files with Jaxer.load(...).) Now how do we call it? If e.g. the jaxer-service folder is directly under your docroot, just send a request to /jaxer-service/tests (and you can append whatever extra path or query items to this path).

How does this work? Jaxer ships with two default dispatchers, both in the extensions folder of the framework: serviceDispatcher.js and rpcDispatcher.js. The first one is what we'll use here (implicitly): it handles requests of the form /path/to/jaxer-service/path/to/myservice/any/additional/data that have jaxer-service in their path, and tries to find the corresponding file on disk. So it looks in your document root for /path.js, then for /path/to.js, then for /path/to/jaxer-service.js, and so on. In our case it will find /path/to/jaxer-service/path/to/myservice.js (or rather /jaxer-service/tests.js) and load it. How does the dispatcher get invoked? That's set in your configApps.js file, or in one of your other configApps-like peer files. By default Jaxer ships with services.configApps.js and that adds an app entry to Jaxer.Config.apps which knows to look for "jaxer-service" in the path and call Jaxer.Extensions.ServiceDispatcher.findFile(...); if it succeeds, the app entry has a "handler" property that returns the path to the file, which Jaxer then loads and executes. Note that this is all handled for you by the default configuration: all you have to do is follow the folder naming conventions. Of course, you can now modify this to suit your needs.

To test this service, I created a simple front-end page called index.html. The source is shown below. Here's what it looks like, including the output from the service:

You can change the method, see which ones support having a body and which do not, etc. Note that the calls are made using your browser's XHR (XMLHttpRequest) capabilities, wrapped in this simple example by the Jaxer client-side framework for convenience.

Note: if you're trying the above in Aptana Studio, it'll work out of the box. If you're trying it with the standalone Jaxer, remove the leading "/" in the URL of the service: use "jaxer-service/tests" instead. That's because Studio needs to do some "magic" remapping between the effective document root and the filesystem.

Here's the source for the front-end page:

index.html:

To summarize, all we needed to do to create the service was to create a JavaScript file somewhere in the document root where its path would contain "jaxer-service". The file will be loaded and executed whenever a request is received for that path (without the ".js" extension). You can add other special paths by adding them to your web server configuration — see the *.httpd.conf files that ship by default with the standalone Jaxer, and to configApps.js and its peers.

In a future blog I'll also show an example of an RPC (Remote Procedure Call) service. Jaxer can also easily consume RESTful services server-side, using e.g. Jaxer.Web.get(...), Jaxer.Web.post(...), Jaxer.Web.put(...), Jaxer.Web.del(...), and Jaxer.Web.head(...) — note that the DELETE action is not invoked via delete(...) because "delete" is a reserved identifier in JavaScript.