As part of a proof of concept I was carrying recently I wanted to deploy and call a simple REST service from Javascript. Ultimately I’ll probably use a Java based framework like Restlet (I’ll be deploying to Google App Engine) but in the meantime the Recess framework proved very useful. Using it’s GUI based tools it’s very easy to setup a persistent data model and expose it as a REST service.

On the client side I used the YUI Javascript framework and JSON as the data interchange format. There didn’t seem to be a direct example of doing this on the Recess website here’s what I came up with.

The only Recess specific requirement is that the Json View is registered on your application controller. More info on views here.

!RespondsWith Layouts, Json

Here’s Javascript code to create a object of type Entry. The object has two attributes, “date” and “data”.

  Y.io("/recess/myjournal/entry/", {
    method: "POST",
    data: {
      "entry[date][day]": Y.one("#entryDay").get("value"),
      "entry[date][month]": Y.one("#entryMonth").get("value"),
      "entry[date][year]": Y.one("#entryYear").get("value"),
      "entry[data]": Y.one("#entryText").get("value")
    },
    on: {
      success: function(transactionid, response, arguments) {
        alert("entry successfully added");
      },
      failure: function(transactionid, response, arguments) {
        alert("add entry failed: " + response.responseText);
      }
    }
  });

For anyone interested in seeing this code in context here’s the page it’s used on. Remember this is just some simple proof of concept stuff so it’s very rough and ready.

<html>
  <head>
    <title>My Journal</title>
    <script type="text/javascript" charset="utf-8" src="http://yui.yahooapis.com/3.1.1/build/yui/yui-min.js"></script>
  </head>
  <body>
    <h2>Entries</h2>
    <ul id="entriesList">
    </ul>

    <form id="newEntryForm" style="display: none">
      <input id="entryDay" type="text" size="2" maxlength="2">
      <select id="entryMonth">
        <option value="1">Jan</option>
        <option value="2">Feb</option>
        <option value="3">Mar</option>
        <option value="4">Apr</option>
        <option value="5">May</option>
        <option value="6">June</option>
        <option value="7">July</option>
        <option value="8">Aug</option>
        <option value="9">Sept</option>
        <option value="10">Oct</option>
        <option value="11">Nov</option>
        <option value="12">Dec</option>
      </select>
      <input id="entryYear" type="text" size="2" maxlength="4">
      <br />
      <textarea id="entryText" rows="10" cols="20"></textarea>
      <br />
      <input id="cancelAddButton" type="button" value="Cancel">
      <input id="saveEntryButton" type="button" value="Save">
    </form>

    <form>
      <input id="refreshButton" type="button" value="Refresh" disabled="true">
      <input id="addButton" type="button" value="Add">
    </form>

    <script>
      YUI().use("node", "io", "json", "datatype-date", "querystring-stringify-simple", function(Y) {
        // Add an entry to list
        var addEntryToList = function(entryDate) {
          var listNode = Y.one("#entriesList");
          var date = Y.DataType.Date.format(entryDate, {format: "%Y-%m-%d"});
          listNode.append("<li>" + date + "</li>");
        };

        // Retrieve entries and add them to the list
        var populateEntriesList = function() {
          Y.one("#refreshButton").set("disabled", true);

          Y.io("/recess/myjournal/entry.json", {
            method: "GET",
            on: {
              success: function(transactionid, response, arguments) {
                var listNode = Y.one("#entriesList");
                listNode.get("children").remove();

                var entries = Y.JSON.parse(response.responseText);

                Y.each(entries.entrySet, function(entry, index, array) {
                  // The date coming from the server will by in seconds. Date() expects milliseconds so * by 1000.
                  addEntryToList(new Date(entry.date * 1000));
                });
              },
              failure: function(transactionid, response, arguments) {
                alert("get entries failed: " + response.responseText);
              },
              end: function(transactionid, arguments) {
                Y.one("#refreshButton").set("disabled", false);
              }
            }
          });
        };

        var showAddEntryControls = function() {
          Y.one("#newEntryForm").setStyle("display", "block");
        };

        var saveEntry = function() {
          Y.io("/recess/myjournal/entry/", {
            method: "POST",
            data: {
              "entry[date][day]": Y.one("#entryDay").get("value"),
              "entry[date][month]": Y.one("#entryMonth").get("value"),
              "entry[date][year]": Y.one("#entryYear").get("value"),
              "entry[data]": Y.one("#entryText").get("value")
            },
            on: {
              success: function(transactionid, response, arguments) {
                var entryDate = new Date(
                  Y.one("#entryYear").get("value"),
                  Y.one("#entryMonth").get("value") - 1,
                  Y.one("#entryDay").get("value")
                );
                addEntryToList(entryDate);
              },
              failure: function(transactionid, response, arguments) {
                alert("add entry failed: " + response.responseText);
              },
              end: function(transactionid, arguments) {
                Y.one("#newEntryForm").setStyle("display", "none");
              }
            }
          });
        };

        var cancelAddEntry = function() {
          Y.one("#newEntryForm").setStyle("display", "none");
        };

        Y.on("available", populateEntriesList, "#entriesList");
        Y.one("#refreshButton").on("click", populateEntriesList);
        Y.one("#addButton").on("click", showAddEntryControls);
        Y.one("#cancelAddButton").on("click", cancelAddEntry);
        Y.one("#saveEntryButton").on("click", saveEntry);
      });
    </script>
  </body>
</html>