Persisting Backbone.js Models and Collections to JAX-RS

There are many competing Javascript framework available; one of the more common and popular framework is Backbone.js. One of its claim to fame over other Javascript frameworks its persistence. (No flames please)

Like other framework, Backbone.js allows you to define models and collections to represent your data; you can then use Backbone.js build in REST features to persist your models and collections to a backend system.

I was recently given the opportunity to use Backbone.js with Java EE, specifically connecting from Backbone.js to a some RESTful resource. I’ll document what I’ve learnt in this blog; the database used here is not the actual database.

The blog will be broken into 3 parts

  • The first part we will look at how to map a model to a JAX-RS root resource and a JPA entity
  • The second part we will extend from mapping a model to a collection
  • The third part we will look at PATCH support

We will be using the following table throughout this blog

customer_table

The customer table (from JavaDB in NetBeans) is mapped to the following JPA entity. Note: for the sake of simplicity, I’ve not mapped all the fields and also I’ve not included any relationship

@Entity @XmlRootElement
public class Customer implements Serializable {
   @Id @Column(name=”customer_id”) private Integer customerId;
   private String name;
   private String addressLine1, addressLine2;
   private String city, state;
   private String email, fax, phone
   private Integer creditLimit;
   //Properties for the above members – not shown
   . . .

The @XmlRootElement annotation allows JAX-RS to automatically convert the a customer entity to JSON (as well as XML).

 

Exposing Customer Entity as a RESTful Resource

We will now use JAX-RS to provide a REST interface for the Customer entity. If you’re not familiar with JAX-RS, see this tutorial.

But before we proceed to code the class, we need to decide the resource identifier; the obvious choice is customer/id where id is the primary key entity.

@Path(“/customer/{id:\\d+}”)
@RequestScoped
public class CustomerResource {
   @PersistenceContext EntityManager em;
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response get(@PathParam(“id”) int custId) {
      Customer customer = em.find(Customer.class, custId);
      if (null == customer)
         return (Response.status(Response.Status.NOT_FOUND)
               .build());
      return (Response.ok(customer).build());
   }
   . . .

Using a REST client for testing Customer resource

rest_get

Click to enlarge

So if you look closely at the resource URI you will notice the following URI

http://localhost:8080/customer/webresources/customer/1

Reading the URI from left to right, the first customer is the the application context. This is followed by webresources. The webresource is the base URI and is used as a prefix for all other resource URI. This value specified in the @ApplicationPath annotation on the Application subclass. If you are using NetBean, this subclass is generated for you and the base URI defaults to “webresources”.

Finally customer/1 is the actual URI of CustomerResource; this is defined in defined in @Path.

 

Mapping to Backbone Model

We will now define a Backbone model to map to customer resource. There are 3 things that we need to know:

  • The first is associating the Model’s id to the JPA’s entity primary key;
  • The second is mapping the resource identifier to JAX-RS root resource’s path.
  • Finally the HTTP method. This will largely depend on what  the model is performing. eg a Model.fetch() will generate a GET method

Lets look at JPA first. By default all Backbone model assumes that the name of the primary key is called id. In our case above, the primary key is called customerId. We have to tell Backbone this otherwise a Backbone will not know the identity of the model if we perform any of the CRUD operations on the model or collections. To map the primary key use the idAttribute.

To retrieve and update backend data, all Backbone models need the URI fo the REST resource. Since we are using JAX-RS, we need to know the URI of the resource. If we map the model to the above defined REST resource, then we need to assign the URI  webresources/customer to the urlRoot property (assuming that our JavaScript is loaded from http://localhost:8080/customer/index.html).

var Customer = Backbone.Model.extend({
   initialize: function() {
      console.log(“created: “ + this.get(“name”));
   },
   idAttribute: “customerId”, //PK mapping
   urlRoot: “webresources/customer” //REST URI mapping
});

To retrieve customerId 1, from we do the following

var customer = new Customer();
customer.set({customerId: 1})
customer.fetch()

The result of the fetch() shown below

result Click to enlarge

If you are unfamiliar with the workings of Backbone.js persistence, read the excellent free book by Addy Osmani here.

I will leave CUD as an exercise for the reader.

 

Mapping to Backbone Collections

One of the concept of RESTful resource is the idea of a container; a container is just a collection of RESTful resource. So we can have a container for Customers. Unlike Java collections, RESTful containers you should not hold the Customer data but a URI to that data. See “Link things together” in this article. So the following is an example of Customer container in JSON

[
   { “id”: 1, “name”: “Jumbo Eagle Corp",
"url":
      “http://localhost:8080/customer/webresources/customer/1”},
   { “id”: 2, “name”: . . .
]

Notice that although there are information like customer id and name, the key is the url which the URI to the actual resource. Lets look at how we can create just such a collection

@Path(“/customers”)
@RequestScoped
public class CustomersResource {
   @PersistenceContext EntityManager em;
   @Context UriInfo ui;
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response get(
         @DefaultValue(“0”) @QueryParam(“start”) int start,
         @DefaultValue(“-1”) @QueryParam(“count”) int count) {
      TypeQuery<Customer> query = em.createNamedQuery(
            “Customer.findAll”, Customer.class);
      query.setFirstResult(start);
      if (count > 0)
         query.setMaxResult(count);
      List<CustomerReference> refs = new LinkedList<>();
      UriBuilder builder = ui.getBaseUriBuilder()
            .path(CustomerResource.class);
      for (Customer c: query.getResultList()) {
         URI uri = builder.clone().build(c.getCustomerId());
         ref.setId(c.getCustomerId());
         ref.setName(c.getName());
         ref.setUrl(uri.toString());
         refs.add(ref);
      }
      GenericEntity<List<CustomerReference>> response =
            new GenericEntity<List<CustomerReference>>(refs){};
      return (Response.ok(response).build());
   }
   . . .

The URI of the Customer container is /customers. The resource allows you to specify a cursor (start) and also the number of records to retrieve (count). You can of course add other query string to the GET like a pattern to match etc.  The rest of the code is quite straight forward.

The CustomerReference is a class to hold the result. It is defined as follows

@XmlRootElement
public class CustomerReference {
   private String id;
   private String name;
   private String url;
   //Getter and setters – not shown

Mapping a collection to /customers URI is very similar to mapping a model. The only difference is you use url instead of urlRoot.

var Customers = Backbone.Collection.extend({
   model: Customer,
   url: “webresources/customers”
});

Once you have setup the model, you can say get 5 Customers starting from position 3

var custSet = new Customers();
custSet.fetch({data: {start: 3, count: 5}});

Partial Model Update with PATCH

One common problem with model is that if you just update one field, Backbone will send all the attributes of that model to the server to be persisted. The HTTP PATCH method allows you to specify only the subset of attributes to send back . Say you want to update the customer’s name and get Backbone to only send that attribute back, set the patch option to true when you are performing a save.

var cust = custSet.get(2);
cust.save({name: "Fred"}, {patch: true});

Backbone will send a PATCH method with /customer/id URL back to the root resource. You can see this in the following

patch Click to enlarge

Now to write JAX-RS support. As I’m using Java EE 7 for this, I get to use new Json classes to map the values in. If you’re using EE 6, use an InputStream as a parameter to read to the method. Then use one of the Java libraries from json.org to parse it to JSON.

First we define a HTTP PATCH method

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH { }

Next we add the method to handle PATH

@Path(“/customer/{id:\\d+}”)
@RequestScoped
public class CustomerResource {
   . . .
   @PATCH
   @Consumes({"application/json"})
   @Produces("application/json")
   public Response updateValues(JsonObject json) {
      //Update record – not shown
      . . .
      return (Response.ok().build());
   }
. . .

The full source including all the missing methods is available here. I have to disguise the source as a Word file because Word Press will not allow me to upload zips. Just download it and rename the suffix to zip. Its a NetBeans project file.

Let me know what you think. Thanks for reading.

Till next time…

Advertisements
%d bloggers like this: