Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Use Dojo's JsonRestStore with your REST services

Learn a simple, generic service implementation

Nick Maynard, Senior Software Engineer, Industry and Business Solutions Team, IBM
Author photo
Nick Maynard works for the IBM Software Solutions Transformation team in Hursley, UK. He specializes in Web programming, Linux, Web services, and business integration technologies. You can contact Nick at nick.maynard@uk.ibm.com.

Summary:  Dojo's JsonRestStore is one of the more advanced options for connecting your REST services to Dojo's data API. If your service does not conform to JsonRestStore's expectations for data structure, the wiring between JsonRestStore and your REST service can become difficult. In this article, learn about a simple, generic service implementation for wiring up nonstandard REST services to JsonRestStore. Learn how to use and extend the implementation for your own services by exploring several examples.

Date:  14 Dec 2010
Level:  Intermediate PDF:  A4 and Letter (186 KB | 10 pages)Get Adobe® Reader®
Also available in:   Chinese  Korean  Japanese  Portuguese

Activity:  25272 views
Comments:  

Introduction

Dojo's data abstraction layer, dojo.data, provides a standard API for Dojo applications to access a variety of back-end services. JsonRestStore, located in the Dojo extensions library DojoX, lets you quickly wire your applications to back-end Representational State Transfer (REST) services.

JsonRestStore is a good solution for linking Dojo to REST services. By default, though, JsonRestStore has expectations for the format of the information passed to, and returned by, the service. The Service Mapping Description (SMD) facility for customizing these interactions is, at best, difficult to understand.

JsonRestStore is a data store that provides full read, write, and notification capabilities through standards based on HTTP/REST interaction with the server using GET, PUT, POST, and DELETE commands. It lets you communicate with server-side database/persistent data storage using the Dojo Data API with JavaScript, and efficiently handles create, read, update, and delete (CRUD) operations.

When Dojo application authors need to communicate with a preexisting back-end REST store, when the store's communications cannot be changed, they use a custom dojox.rpc.Service implementation.

In this article, learn a simple, generic service implementation for wiring up nonstandard REST services to JsonRestStore. Explore how to use and extend it for your own services by walking through a practical example.

To download the source code for this article, see the Download section.


Example service implementation

Develop skills on this topic

This content is part of a progressive knowledge path for advancing your skills. See Get started with Dojo development

EasyRestService provides implementations for the four REST operations: POST (create), GET (read), PUT (update), and DELETE (delete). For each activity, it provides the ability to hook in before and after invocation of the service method. The invocation life cycle is:

  1. Construct a standard Dojo XHR invocation structure containing the URL, arguments, and data format metadata.
  2. Call the arguments transformer for the method. This method can alter the invocation structure (including the path), and can be used to transform the invocation structure to conform with the REST service's expectations.
  3. Populate the putData/postData/deleteData/getData node of the invocation structure by translating the content node to a JSON representation.
  4. Call the XHR method with the altered XHR invocation structure.
  5. Add a results transformer to the XHR callback. This method can alter the data structure of the results. Use it to transform the results into the structure expected by JsonRestStore.

Listing 1 shows the example code.


Listing 1. EasyRestService source code source
        
dojo.provide("com.ibm.developerworks.EasyRestService");

(function() {
  var pa = com.ibm.developerworks.EasyRestService = function (path, serviceImpl, schema) {
    // Enforce the dojox.rpc.Rest trailing slash functionality
    path = path.match(/\/$/) ? path : (path + '/');
    
    // A dojox.rpc.Service implementation is a function with 3 function members
    var service;
    // GET function
    service = function(id, args) {
      return _execXhr("get", id, args);
    };
    // POST function member
    service['post'] = function(id, value) {
      return _execXhr("post", id, value);
    };
    // PUT function member
    service['put'] = function(id, value) {
      return _execXhr("put", id, value);
    };
    // DELETE function member
    service['delete'] = function(id) {
      return _execXhr("delete", id);
    };
    
    // Generic XHR function for all methods
    var _execXhr = function(method, id, content) {
      // Transform the method string
      var methodCapitalised = method.substring(0,1).toUpperCase() 
        + method.substring(1).toLowerCase();
      var methodUpperCase = method.toUpperCase();
      var methodLowerCase = method.toLowerCase();
      
      // Get the transformer functions
      var argumentsTransformer = service["transform" + methodCapitalised + "Arguments"];
      var resultTransformer = service["transform" + methodCapitalised + "Results"];
      
      // Construct the standard query
      var serviceArgs = {
        url : path + (dojo.isObject(id) ? '?' + dojo.objectToQuery(id) : 
          (id == null ? "" : id)), 
        handleAs : "json",
        contentType : "application/json",
        sync : false,
        headers : { Accept : "application/json,application/javascript" }
      };
      
      // Transform the arguments
      // NOTE: argumentsTransformer has a reference to "service"
      serviceArgs = argumentsTransformer(serviceArgs, arguments);

      // Copy the content into the appropriate *Data arg
      // getData, putData, postData, deleteData
      // NOTE: If you want your arguments transformer to edit the *Data arg directly, 
      // move the arguments transformer invocation to after this call 
      serviceArgs[methodLowerCase + 'Data'] = content;
            
      // Kick off the call
      var xhrFunction = dojo['xhr' + methodCapitalised];
      var deferred = xhrFunction(serviceArgs);
      // Add our result transformer
      // NOTE: resultTransformer has a reference to "service" too
      deferred.addCallback(dojo.partial(resultTransformer, deferred));
      
      return deferred;
    };

    // Mix in the service hooks
    // Uses a "default" implementation that does nothing
    // Service hooks will have a reference to the "service" object in their context
    dojo.mixin(service, 
      new com.ibm.developerworks.EasyRestService.DefaultHooks(), 
      serviceImpl);
    
    // Now remove any default _constructor() methods
    // This is necessary as the JsonRestStore stack uses _constructor() differently
    delete service['_constructor'];
    // Remove the declaredClass member if it has been added
    delete service['declaredClass'];
    
    // Save the path away
    service.servicePath = path;
    // Save the schema
    service._schema = schema;
    
    return service;
  };
})();

dojo.declare("com.ibm.developerworks.EasyRestService.DefaultHooks", null, {
  transformGetArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformPutArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformPostArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformDeleteArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformGetResults: function(deferred, results) {
    /*
     * JsonRestStore expects the following format:
     * [
     *  { id: "1", ... },
     *  { id: "2", ... },
     *  ...
     * ] 
     */
    return results;
  },
  transformPutResults: function(deferred, results) {
    /*
     * JsonRestStore does not expect any specific content here
     */
    return results;
  },
  transformPostResults: function(deferred, results) {
    /*
     * JsonRestStore expects:
     * 1) A "Location" response header with location of the new item.
     * 		From the Dojo API:
     * 			The server’s response includes a Location header
     * 			that indicates the id of the newly created object.
     * 			This id will be used for subsequent PUT and DELETE 
     * 			requests. JsonRestStore also includes a 
     * 			Content-Location header that indicates the temporary
     * 			randomly generated id used by client, and this 
     * 			location is used for subsequent PUT/DELETEs if no 
     * 			Location header is provided by the server or if 
     * 			a modification is sent prior to receiving a response 
     * 			from the server.
     *    NB: There is no JS method for altering response headers.  
     *      You might wish to try overriding the 
     *      deferred.ioArgs.xhr.getResponseHeader() method with your
     *      own implementation.
     * 2) The new item in the following format:
     * { id: "1", ... }
     */
    return results;
  },
  transformDeleteResults: function(deferred, results) {
    /*
     * JsonRestStore does not expect any specific content here
     */
    return results;
  }
});
      

The code in Listing 1 replicates the default functions of dojox.rpc.Rest. It can be used with JsonRestStore, as shown in Listing 2.


Listing 2. Default use of EasyRestService with JsonRestStore
        
dojo.require("com.ibm.developerworks.EasyRestService");
dojo.require("dojox.data.JsonRestStore");

var store = new dojox.data.JsonRestStore({
  service: new com.ibm.developerworks.EasyRestService("https://mydomain.com/restservice"),
  idAttribute : "id"
});

By default, an EasyRestService instance does not alter the arguments and results in any way. Instead of altering DefaultHooks to perform required arguments and results transformation, EasyRestService provides a mechanism for overriding these transformers on a per-instance basis.


Customizing EasyRestService

EasyRestService provides a simple mechanism for the provision of custom transformers. The example in Listing 3 alters the behavior of EasyRestService to log the GET invocation structure before execution.


Listing 3. Customizing EasyRestService
        
dojo.require("com.ibm.developerworks.EasyRestService");

var transformers = { 
  transformGetArguments: function(args) { 
    console.log(args); 
    return args; 
  }
};
var service = new com.ibm.developerworks.EasyRestService(
  "https://mydomain.com/restservice", transformers);

Similarly, all transformers in DefaultHooks can be overridden on a per-instance basis.

Example

Create two instances of EasyRestService: one for a compliant service and one for a noncompliant service. The example uses them as the service providers for two instances of JsonRestStore, and issues a basic fetch against the stores.

The data stores

In Listing 4 and Listing 5, the services are mocked up using two read-only files containing JSON structures.


Listing 4. Compliant REST service, compliantService.json
        
[
	{ id: 1, name: "Phil" },
	{ id: 2, name: "John" }
]


Listing 5. Noncompliant REST service, noncompliantService.json
        
{ items : [
	{ id: 1, name: "Phil" },
	{ id: 2, name: "John" }
] }

Store and service interaction code

JavaScript will instantiate and query the stores using the code in Listing 6.


Listing 6. JavaScript code to interact with the stores
        

// Create a store using a service that needs no transformations
compliantStore = new dojox.data.JsonRestStore({
  service : new com.ibm.developerworks.EasyRestService(
      "./compliantService.json"),
  idAttribute : "id"
});

// Cause an async fetch from the compliant service
dojo.create("p", {
  innerHTML : "Requesting from compliant service"
}, dojo.body(), "last");
compliantStore.fetch({
  onComplete : function(items, request) {
    console.log(items);
    // Log the number of items fetched
    dojo.create("p", {
      innerHTML : "Got " + items.length + " items from compliant service."
    }, dojo.body(), "last");
  }
});

// Create a store using a service which needs transformations
// to interpret the results
noncompliantStore = new dojox.data.JsonRestStore({
  service : new com.ibm.developerworks.EasyRestService(
      "./noncompliantService.json", {
        transformGetResults : function(deferred, results) {
          // This store wraps its results in an items object
          // so return the items object
          return results.items;
        }
      }),
  idAttribute : "id"
});

// Cause an async fetch from the noncompliant service
dojo.create("p", {
  innerHTML : "Requesting from noncompliant service"
}, dojo.body(), "last");
noncompliantStore.fetch({
  onComplete : function(items, request) {
    console.log(items);
 	// Log the number of items fetched
    dojo.create("p", {
      innerHTML : "Got " + items.length
          + " items from noncompliant service."
    }, dojo.body(), "last");
  }
});


Summary

In this article, you learned how to wire up your own REST services to JsonRestStore. The examples showed a simple method of transforming your service interface to provide the signatures that JsonRestStore requires. For information on the full data structure expected by JsonRestStore, you should refer to the comments in DefaultHooks, the JsonRestStore documentation at DojoCampus, and the API documentation.

For information on the full data structure expected by JsonRestStore, see the Resources section.



Download

DescriptionNameSizeDownload method
EasyRestService code sample - project archiveEasyRestServiceExample.zip6.5KBHTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Author photo

Nick Maynard works for the IBM Software Solutions Transformation team in Hursley, UK. He specializes in Web programming, Linux, Web services, and business integration technologies. You can contact Nick at nick.maynard@uk.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=600700
ArticleTitle=Use Dojo's JsonRestStore with your REST services
publish-date=12142010

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers