As I’ve used Sencha Fiddle, I’ve really come to enjoy how simple it makes testing ideas, exploring aspects of Ext JS, and debugging my code. One of the really nice features is the ability to simulate AJAX requests. While this is REALLY nice for simulating the loading of remote data, one place where it has a gap is in testing a fully-rounded workflow…for example, loading data from the server, modifying it, sending it back to the server, and receiving a successful response.

For these kinds of tasks, there a few options. First, you can set up your own server which your Fiddle code can talk to, either via CORS or JSONP. While this works, it’s a pain. Most of the time the stuff I’m doing in Fiddle is not that involved, so having to mess with a server just for remote communications is a little onerous.

Another option is Parse.com. Using this service, you can create and manage cloud-based data structures. And using their RESTful API, you can retrieve this data, as well as all the other standard CRUD operations.

Integrating Parse.com with your Ext JS app (or Sencha Touch app) is RIDICULOUSLY easy. I’m going to assume you’ve already set up your Parse.com account…if you haven’t, go get one now.

Ready? Okay, let’s do this.

Step 1: Understand the API URLs

The Parse.com RESTful API is pretty straightforward. All of your base class URLs are going to follow this syntax:

https://api.parse.com/1/classes/{className}

So let’s say that you have a class called “color”. The RESTful API endpoints will look like this:

  • https://api.parse.com/1/classes/color (GET – List)
  • https://api.parse.com/1/classes/color/{objectId} (GET – Single Result)
  • https://api.parse.com/1/classes/color (POST – Create)
  • https://api.parse.com/1/classes/color/{objectId} (PUT – Update)
  • https://api.parse.com/1/classes/color/{objectId} (DELETE – Remove)

For those already using it, you’ll immediately notice something great…this is precisely the way that the Ext JS Rest proxy will format the api method URLs out-of-the-box. Perfect! Job done.

Using this knowledge, we can configure the proxy for app’s base model:

Ext.define('CarTracker.model.Base', {
    extend: 'Ext.data.Model',
    ...
    schema: {
        namespace: 'CarTracker.model',
        proxy: {
            type: 'rest',
            url: 'https://api.parse.com/1/classes/{entityName:lowercase}',
            useDefaultXhrHeader: false,
            withCredentials: false,
            headers: {
                'X-Parse-Application-Id': 'XXXXX',
                'X-Parse-REST-API-Key': 'XXXXX',
                'Content-Type': 'application/json'
            }
            ...
        }
    }
});

A few things to note here.

First, since we’re using a schema (aka, a manager for related entities), we can use the {entityName:lowercase) syntax in our URL to provide a nice template for defining all the RESTful endpoints for models within this schema. So if our concrete class of “Color” belongs to this schema, the generated URL will be: ‘https://api.parse.com/1/classes/color’

Next, note the headers configuration. This is critical in order for the API requests to work. Obviously you will substitute the Application ID and API Key with the values you get as a result of setting up your datastore with Parse.com

Step 2: Configure the Reader

Next, we need to configure our proxy’s reader to deal appropriately with the data coming back from Parse.com’s API. Let’s look at the code, and then I’ll explain:

reader: {
    type: 'json',
    rootProperty: 'results',
    transform: function( data ) {
       return data.results ? data.results : {results:data};
    }
}

The default rootProperty is configured to “results”. This is the wrapper that Parse.com adds to list requests. It’s possible to change it (I think) through a lot of trouble and *code* on the Parse.com end. Save yourself some headaches, though, and just use the default!

You’ll notice also that we’ve implemented a custom transform function for our reader. This is necessary because in the instance of single-result retrieval, Parse.com *doesn’t* return the result in the “results” wrapper. So then, our transformer simply deals with this and adds the wrapper so that the reader will deal with both scenarios the same.

Step 3: The Writer

The writer configuration is pretty sparse. The only config we need to add is this:

writeRecordId: false

This is needed because by default Ext JS will send the phantom record ID _as data_ when making POSTs. Parse.com does not particularly appreciate this, so we simply tell Ext JS not to send it. This won’t harm other indempotent requests, like PUT/DELETE, since the record id will be appended to the URL while not being added to the data of the request.

Step 4: Default Fields

All classes that you create in your Parse.com datastore will have the following columns:

  • objectId
  • createdAt
  • updatedAt

You can’t rename them (in Parse.com), so you can either deal with field mappings in your models, or just deal with the names as they are. Your choice.

Step 5: Putting it all Together

Pretty easy, right? Here’s our full base model definition with all configuration in place:

Ext.define('CarTracker.model.Base', {
    extend: 'Ext.data.Model',
    idProperty: 'objectId',
    fields: [{
        type: 'string',
        name: 'objectId'
    }, {
        type: 'date',
        name: 'updatedAt',
        persist: false
    }, {
        type: 'date',
        name: 'createdAt',
        persist: false
    }],
    schema: {
        namespace: 'CarTracker.model',
        proxy: {
            type: 'rest',
            url: 'https://api.parse.com/1/classes/{entityName:lowercase}',
            useDefaultXhrHeader: false,
            withCredentials: false,
            headers: {
                'X-Parse-Application-Id': 'XXXXXX',
                'X-Parse-REST-API-Key': 'XXXXXX',
                'Content-Type': 'application/json'
            },
            reader: {
                type: 'json',
                rootProperty: 'results',
                transform: function(data) {
                    return data.results ? data.results : {
                        results: data
                    };
                }
            },
            writer: {
                type: 'json',
                writeRecordId: false
            }
        }
    }
});

Conclusion

The cool thing about Parse.com is that, for Ext JS developers, it works on several levels. First, in terms of dealing with remote requests and persistence in the context of Sencha Fiddle, it provides an easy-to-maintain datastore. However, as  you being to use Parse.com, you may find that it is robust enough to be THE datastore for your app as well. That’s what it’s there for, and given how simple it is to integrate with Ext JS, why not?

Happy coding!