An App in an Afternoon: Sencha Touch & StackMob

Update: August 21, 2012

Since this blog post was initially released in June, StackMob has made some significant changes to their development environment. In particular, rather than relying on a local runner within the stackmob gem, they now ship a Python web server. This blog post has been updated to reflect these changes. Most of the content is the same; the only sections that have changed are “Connecting Sencha to StackMob” and “Deploying to Production”. We have also updated the sample project in GitHub.

Some Cool Tools

Sencha Touch is a fantastic JavaScript framework for building mobile web applications. It is great for rapid development because it comes stocked with all the standard UI components you want, and makes connecting to your API painless. We love using Sencha Touch to knock out a quick prototype, but the really stellar thing about this framework is that it is structured to scale (from a development perspective). The MVC structure of Sencha Touch 2 applications encourages developers to build a codebase that is maintainable even as it grows large. With flexible data store options, you can prototype an app with no server component (backed by local storage). Then, once you refine your idea and decide you want to turn it into a real app, you can drop in a proper API and reuse nearly all of the prototype code.

StackMob is a rapidly improving option for building an entire REST API in minutes. It allows you to define your data model and expose CRUD actions all through a simple UI. For those of you with “real world” problems, and “non CRUD actions” they provide a really simple way to develop custom endpoints with our good friend, Mr. Java. This week, they released OAuth 2 and shiny new access control features which remove the last few hurdles to using StackMob for grown up projects.

If we combine these tools we should be able to build pretty powerful applications in very little time. This isn’t easy to do out-of-the-box, so we are going to write a series of blog posts describing how to use these tools together.

Let’s start off simple, and build a silly little app that we can run in development mode (on our local machine) and in production mode (hosted by StackMob). This process will show how to get started with Sencha Touch 2 and StackMob without getting too deep into the complicated features necessary to build something truly useful (we will do this in future posts).

Let’s Build an App!

We are going to build a mobile web application that stores a list of meats. Users can load this app and browse through a list of meats that other users have created. They can even add some new ones if they want. Very silly. Very simple.

Sencha Touch

Let’s start by building a Sencha Touch application to show the list of Meats in the system and add new ones. The best (and fastest) way to get started writing your Sencha Touch application is to download and install Sencha’s great Sencha SDK Tools and Sencha Touch. You’ll also need to set the environment variable SENCHA_SDK_TOOLS_2_0_0_BETA3 to be the SDK’s installation directory (e.g. /Applications/SenchaSDKTools-2.0.0-beta3).

Once you’ve done that, starting a new project is very easy, just use the sencha command to generate a new app:

    cd PATH_TO_SENCHA_TOUCH && /PATH_TO_SENCHA_SDK/sencha generate app StackMobDemo ~/StackMobDemo

This will generate a skeleton Sencha Touch app called StackMobDemo, located in the StackMobDemo directory in your home directory. Feel free to rename the app or generate it in another location by varying these parameters. If you’d like to see the result, launch a web server from within the directory you just created by running

    cd ~/StackMobDemo && python -m SimpleHTTPServer

and point your browser to http://localhost:8000. Now we’re all ready to write some code. This tutorial covers the Sencha Touch code at a fairly high level; if you’re completely unfamiliar with Sencha Touch, we highly recommend checking out the Sencha Touch Learning Center and the Sencha Touch 2 API Docs.

The Data Model

The data model of our app is very simple: we have a list of meats, and each meat has an ID and a name. Defining such a simple model in Sencha Touch is also quite simple; we create the model in ~/StackMobDemo/app/model/Meat.js like so:

Ext.define('StackMobDemo.model.Meat', {
    extend: 'Ext.data.Model',

    config: {
        idProperty: 'meat_id',
        fields: [
            {name: 'meat_id', type: 'auto', persist: false},
            'name'
        ]
    }
});

This should be pretty straight-forward. We extend the base model class to have two fields (meat_id and name). Since our ID will be stored in the meat_id field, we tell Sencha Touch to handle it specially (in particular, we want the IDs to be generated in the server — persist: false tells Sencha Touch not to send up automatically-generated IDs when creating new records). Now that we have a meat model, we need a data store for meats. Create the store in ~/StackMobDemo/app/store/Meats.js:

Ext.define('StackMobDemo.store.Meats', {
    extend: 'Ext.data.Store',

    config: {
        model: 'StackMobDemo.model.Meat',
        autoLoad: true,
        sorters: 'name',
        proxy: {
            type: 'rest',
            url: 'meats.json'
        }
    }
});

Our data store needs to know a few key pieces of information. Obviously, it needs to know what kind of data it will be storing (our newly-created Meat model). We tell it to fetch data automatically, and we tell it to sort the meats alphabetically by their name field. Finally, we define the store’s proxy. In Sencha Touch, proxies are responsible for reading and writing data to and from external sources. In this case, we want our proxy to be for a RESTful web service, and we point that proxy at meats.json. This is going to be our dummy data source that we use temporarily, before we get the StackMob components set up. Let’s make that file now, and place it at ~/StackMobDemo/meats.json:

[{
    "meat_id": "0",
    "name": "Bacon"
},{
    "meat_id": "1",
    "name": "Haggis"
},{
    "meat_id": "2",
    "name": "Blood Sausage"
}]

Now we have a model, a store to keep the model instances in, and a dummy set of data with three meats in it. Awesome. What would be more awesome, though, would be to actually see something happen in the browser. Let’s play with some views next.

The Views

First let’s create a list view in ~/StackMobDemo/app/view/List.js:

Ext.define('StackMobDemo.view.List', {
    extend: 'Ext.List',

    id: 'meatList',

    config: {
        title: 'Meats',

        store: 'Meats',
        itemTpl: '{name}'
    }
});

Again, nothing too complex here. We extend the default Ext.List class, we tell the list which store to use (Sencha Touch is smart enough to figure out that by 'Meats' we are really referring to StackMobDemo.store.Meats), and we use itemTpl to create a template for the individual list items (here, each row will simply contain a single string: the name of the meat). The other two options (id and title) will come into play later, but spoiler alert, id is used to reference the list, and title will be the title displayed for the list.

The last little bit of plumbing that we need to do is to actually plug all of these classes together and display the list in the app. To do this, we need to do two things. First, let’s replace the ~/StackMobDemo/app/view/Main.js view that was automatically generated with some code to display the list (just a simple navigation view with one child view — our list):

Ext.define("StackMobDemo.view.Main", {
    extend: 'Ext.navigation.View',

    requires: ['StackMobDemo.view.List'],

    id: 'mainView',

    config: {

        items: [{
            xclass: 'StackMobDemo.view.List'
        }]
    }
});

And finally, in ~/StackMobDemo/app.js, we need to make sure that we require all the classes that we have just defined. For brevity, we’ve snipped most of the code out in the snippet below to highlight the specific lines we need to add, but you should keep the rest of the code. It’s important.

// ... snip ...
Ext.application({
    // ... snip ...

    models: ['Meat'],
    views: ['Main'],
    stores: ['Meats'],

    // ... snip ...
});

There. That should be everything we need to display our list of meats. Refresh that browser window and drink it in.

Add Some Interactivity

Lists are pretty cool, but static lists are boring. Let’s augment our list to let the user add a meat. To begin, we’ll add a button next to the title. Open ~/StackMobDemo/app/view/Main.js back up, and add a button to the navigation bar. Now it should look something like this:

Ext.define("StackMobDemo.view.Main", {
    extend: 'Ext.navigation.View',

    requires: ['StackMobDemo.view.List'],

    id: 'mainView',

    config: {

        items: [{
            xclass: 'StackMobDemo.view.List'
        }],

        navigationBar: {
            items: [{
                xtype: 'button',
                id: 'addButton',
                iconCls: 'add',
                iconMask: true,
                align: 'right'
            }]
        }
    }
});

That will give us a slick “+” button. To make the button actually do something, we need to put the “C” in MVC and add a controller to our application. Let’s define ~/StackMobDemo/app/controller/Application.js:

Ext.define('StackMobDemo.controller.Application', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            addButton: '#addButton',
            meatList: '#meatList'
        },
        control: {
            addButton: {
                tap: 'onAddButtonTap'
            }
        }
    },

    onAddButtonTap: function() {
        Ext.Msg.prompt('Add Meat', 'What kind of meat?', this.onSubmitMeat, this, false, null, {
            autoCapitalize: true,
            placeHolder: 'No vegetables, please...'
        })
    },

    onSubmitMeat: function(buttonId, value) {
        if (buttonId == 'cancel') {
            return false;
        }
        var newMeat = Ext.create('StackMobDemo.model.Meat', {
            name: value
        });
        var store = this.getMeatList().getStore();
        store.add(newMeat);
        store.sync();
    }
});

Controllers in Sencha Touch can look a bit intimidating at first. There are four main things going on here:

  • refs. refs create getters for you that return the components matching the supplied Component Query. Here, we are defining two refs. The addButton ref finds any component whose ID is “addButton” (note that this is the ID that we supplied when we created our add button). Now if we call this.getAddButton() in the controller, we will get a programmatic handle to our add button. Similarly, the meatList ref matches any component whose ID is “meatList”, and we can get it by calling this.getMeatList().
  • control. control is how we can add event handlers to components that we defined above in refs. Here, we are telling the controller that any time our addButton component firs the tap event, it should call the onAddButtonTap method.
  • onAddButtonTap. This is the method that we attached to the addButton‘s tap event. It opens a simple prompt, asking the user to input a new meat. When the prompt is submitted, we call this.onSubmitMeat.
  • onSubmitMeat. This is the method that handles the prompt submission. If the prompt was not cancelled, it creates a new meat instance, then uses our refs that we defined to get the meat list, and finally adds the meat to the meat list’s store and syncs the store.

Once we’ve wrapped our heads around all that, all we have to do is register this controller with our application. Go back into ~/StackMobDemo/app.js and add the controller alongside the model, view, and store:

// ... snip ...
Ext.application({
    // ... snip ...

    models: ['Meat'],
    views: ['Main'],
    controllers: ['Application'],
    stores: ['Meats'],

    // ... snip ...
});

If we refresh our browsers now, we should see that we have a “+” button which, when tapped, opens a prompt. When the user inputs a new meat and submits the prompt, the new meat gets added to our list. Excellent. Well, almost excellent. If you look in Web Inspector, you’ll notice that we get JavaScript errors when we add new meats; namely, we are attempting to POST and getting 501′s. This makes a lot of sense: we told our data store to treat the local file meats.json as if it were a RESTful service, so when we add a new meat, Sencha Touch is POSTing to a JSON file. This doesn’t really… work. What we really want is not a static file for our data source, but rather a server. “But,” you say, “aren’t servers so hard?” Not with StackMob.

StackMob

We’re going to use StackMob to get a simple API running in just a few minutes. Get yourself psyched up for how awesome this is. When you’re ready, read on.

Create a free StackMob account. During your registration, you are prompted to select a name for your first StackMob application. Consider calling it meatapp (application names don’t need to be unique). When you login you should be taken to the getting started page. Let’s hold off on looking through these docs yet (though I recommend you read them, the docs are great). We’re going to jump right into defining the meat of our API (cheap pun intended).

Creating A Schema

Let’s define our API endpoints and data model. This is the absurdly cool part of StackMob. From StackMob’s docs, click on Manage Schemas on the left hand side under Build. StackMob creates a user schema for you by default, we’re going to ignore this for this blog and create our own schema called meat.

  • Click the Create New Schema button in the top right.
  • Name the schema meat and add a String field called name.
  • We want to allow create and read in an “Open” way, so that we can ignore user auth in this simple post
  • We are not supporting Update and Delete, so select “Not Allowed” for those options.
  • Click Save Schema and you’re done! The configuration should match the image below.

StackMobSchemaScreen

That’s it, you’ve just created an endpoint for creating and retrieving different kinds of meat. Take a moment to reflect upon how easy that really was.

Connecting Sencha to StackMob

The StackMob Python Web Server

Now we want to make our Sencha Touch app connect to our shiny, new StackMob API. StackMob has a Backbone.js based SDK which we could probably shoehorn into our Sencha Touch app, but it would be messy. Instead, we’d like to use Sencha’s data services to talk to StackMob’s REST API directly.

There is one hiccup with connecting to StackMob locally. StackMob hosts your API in the magical cloud, which means an AJAX request to their server from a page hosted at localhost would violate browser’s same origin policy. They don’t currently support CORS so that’s not an option. The StackMob Python Web Server is a proxy server that solves this problem. It proxies requests from your localhost to StackMob’s servers.

Follow the instructions in Step 2 of the Getting Started wizard to get the local server running.

First, download the web server.

Next, move the Python script to the root of your Sencha Touch application.

On this page, they provide you a snippet of JavaScript that you would use to connect to StackMob if you were using the Backbone StackMob SDK. We won’t be using it, but we DO need to use the public key it contains — make a note of it before we move on. If you ever need to find it again later, you can always look it up in the Manage App Info section of the StackMob Dashboard.

<script type="text/javascript">
StackMob.init({
    appName: 'your app name',
    clientSubdomain: 'your client subdomain',
    publicKey: 'your public key', // <-- We will need this value
    apiVersion: 0
});
</script>

Now if you execute python stackmobserver.py, the proxy will be running on localhost:4567. This server will serve your Sencha Touch app, as well as proxy API requests to StackMob once we configure it with your public key and point the app at our API endpoints. Let’s do that next.

Point Sencha at StackMob

Now we are going to modify our proxy to connect to StackMob via the the proxy url. Out of the box, Sencha Touch provides a proxy for communicating with RESTful services. This is the proxy we set up. Unfortunately, StackMob expects requests to be prepared slightly differently than the way the stock Sencha Touch REST proxy prepares them. To get Sencha Touch and StackMob to communicate gracefully in every situation requires enough modification to Sencha Touch that the topic deserves its own blog post, which will be coming in the near future. In the meantime, it is fairly trivial to get the limited functionality in our app to work. So let’s do that.

We want most of the functionality of Sencha Touch’s REST proxy, but with a few tweaks. So we create a subclass in ~/StackMobDemo/app/data/proxy/StackMob.js;

Ext.define('StackMobDemo.data.proxy.StackMob', {
    extend: 'Ext.data.proxy.Rest',
    alias: 'proxy.stackmob',

    config: {
        headers: {
            'Accept': 'application/vnd.stackmob+json; version=0',
            'X-StackMob-Proxy-Plain': 'stackmob-api',
            'X-StackMob-API-Key': 'your public key',  // <-- Insert your development public key here
            'X-StackMob-User-Agent': 'StackMob (JS; 0.2.1)/stackmobdemo'
        },

        // Disable cache busting / paging for now, since StackMob rejects the GET params added by ServerProxy.
        // TODO: re-implement these in a StackMob-compliant manner (request headers)
        noCache: false,
        pageParam: false,
        startParam: false,
        limitParam: false
    }
});

StackMob requires a few headers to be added to the request, and it does not support paging and limits as request params (it wants them to be headers as well). Luckily, we don’t need any of that for our app, so we can just turn them off. We will go through actually reimplementing them in a future post. Note that, in the X-StackMob-User-Agent header, the bit after the slash should match the name of your StackMob application. Ours is called stackmobdemo, but yours might not be. Be sure to set the X-StackMob-API-Key header to be your development public key. Again, you can find this in the Manage App Info section of the StackMob Dashboard.

To use the proxy, we simply swap out the old one in our ~/StackMobDemo/app/store/Meats.js for this new one (don’t forget to also require the class). It should now look like this:

Ext.define('StackMobDemo.store.Meats', {
    extend: 'Ext.data.Store',

    requires: ['StackMobDemo.data.proxy.StackMob'],

    config: {
        model: 'StackMobDemo.model.Meat',
        autoLoad: true,
        sorters: 'name',
        proxy: {
            type: 'stackmob',
            url: 'meat'
        }
    }
});

That’s it. Refreshing the app in your browser, you should now be able to add meats that get persisted to the server. Add a meat. Refresh the app, it should still be in the list. If you open StackMob’s Object Datastore Browser, you should be able to find your meat there as well.

Deploying to Production

Now that we have everything running in development mode, let’s get it running in production. This means two things: publishing your API to a production URL and having StackMob host your HTML5 app. The hosting is necessary in production for the same reason the Local HTML Runner was necessary in development: to appease the browser’s same origin policies.

The first step is to deploy our API to production. This is quick and painless, but will require a credit card. We will be selecting an absolutely FREE plan, but you need a credit card on file to create the account (presumably in case you go over your free API call limits and such).

To deploy your API, click on Deploy your App under Deploy. Select Deploy API and continue. You’ll be prompted to select a plan. I recommend selecting the free one for now. After you enter your credit card information, you will be able to continue your deployment.

When prompted, I created a new snapshot called “Test Production Deploy” and created API Version 1. All that’s left is adding a description and clicking deploy. If you’d like more info on this process, check out their docs on deploying.

Next we want to deploy our HTML5 app. To do this, we need to tie our StackMob account to a Github repo holding our Sencha Touch application source (yes, you have to use Github for HTML5 deployment to work). You can follow the excellent instructions StackMob offers by clicking on Manage HTML5 under Build.

Once you are connected to Github, return to the HTML5 settings page and click the checkbox to enable HTML5 serving and API proxying in development.

EnableHTML5Dev

Now you should be able to see your app in development mode, hosted by StackMob. Click on the link called dev link in the Enable/Disable section (from the image above).

Once you’ve confirmed that the deployment works in dev mode, enable production deployment by returning to the html settings page and select “Enable HTML5 in production” as shown below.

EnableHTML5Prod

After you do this, and before you actually deploy, we’ll have to make one change to our Sencha Touch code and commit that change. In production mode, the server expects the client to send up the correct API version. In our case, this means we have to change “0″ to “1″ in our proxy. We also need to swap out our development public key for the production public key (which can also be found in the Manage App Info section of the StackMob Dashboard.). The new proxy should look like this:

Ext.define('StackMobDemo.data.proxy.StackMob', {
    extend: 'Ext.data.proxy.Rest',
    alias: 'proxy.stackmob',

    config: {
        headers: {
            'Accept': 'application/vnd.stackmob+json; version=1',
            'X-StackMob-Proxy-Plain': 'stackmob-api',
            'X-StackMob-API-Key': 'your public key',  // <-- Insert your production public key here
            'X-StackMob-User-Agent': 'StackMob (JS; 0.2.1)/stackmobdemo'
        },

        // Disable cache busting / paging for now, since StackMob rejects the GET params added by ServerProxy.
        // TODO: re-implement these in a StackMob-compliant manner (request headers)
        noCache: false,
        pageParam: false,
        startParam: false,
        limitParam: false
    }
});

Note that the only changes were setting version=1instead of version=0, and changing the development public key to the production public key.

Add, commit and push this change to Github. StackMob should automatically pick up the change, and if you reload your development url (the one hosted by StackMob, not the one at localhost), the API requests to StackMob should fail with a response body describing that development keys can only be used with version 0. This is expected. It is quite unfortunate that you have to break your development app in version control if you want to deploy to production, but it is an irritation we’ll deal with for now (hopefully StackMob smooths this out soon).

Return to the Deploy your App page. This time, the Deploy HTML option should be enabled. Select it and click Deploy. The page will indicate it is deploying, but not autorefresh when it is done. Wait a moment for all the bits to fly from one cloud to another, and then revist the html settings page. Under the Enable/Disable section, there should now be a link to your production app called prod link. If you click this, you should see your app running in production. Cool!

Making Something Meaningful

Hopefully this exercise has been interesting and has you itching to use these tools together for a real project. If you want a fully working version of the Sencha Touch app, clone our Github repo. What we’ve done here is a good start, but it doesn’t cover everything you’ll need to create a real application that can authenticate users and take full advantage of the features StackMob offers. Keep a close eye on the Palomino Labs blog and our Github page for more developments. We plan to release an open source Sencha Touch user extension for connecting to StackMob shortly. This will help you use StackMob in your Sencha Touch app without worrying about headers and tokens and the like.

Posted by Tyler Wolf

Skilled in Rails, PHP, Java, JavaScript, and iOS development, Tyler began building dynamic websites when he was 14. Tyler’s expertise includes web application security and design and developing recommendation and rating systems. While at Genius.com, Tyler helped develop their Java integration with Salesforce.com and Genius’ own external API. Following Genius.com, Tyler was a Senior Software Developer at Tello where he led a major upgrade of a Rails 2 application to Rails 3, helped develop both the native iPhone and Sencha Touch mobile applications, and the OAuth based Rails API consumed by the mobile applications. While a student at Harvey Mudd College, Tyler co-founded Mudd Software Designs, an organization that builds software systems as a service to the college community while teaching underclassman the principles of good software design and product management.

About Palomino Labs

Palomino Labs unlocks the potential of software to change people and industries. Our team of experienced software developers, designers, and product strategists can help turn any idea into reality.

See the Palomino Labs website for more information, or send us an email and let's start talking about how we can work together.