AIR + Heroku + Pusher

In this post I want to demonstrate an AIR client, that makes a remote call to a Node.js app running on Heroku, and receives a real-time update from Pusher.

Over the last 6 years I’ve built a number of enterprise RIAs using Flex/AIR and LiveCycle Data Services. I’m not alone in this, there are big investments out there in the Adobe technology stack and some incredible apps. While the HTML5 family offers comparable capabilities it’s not easy to turn your back on an investment. It takes time to pivot.

I’ve been thinking about how we can combine these technology stacks as they aren’t mutually exclusive. One strategy I’ve been looking at is using Node.js on the server-side. If I’m using  Node.js then I am using Heroku.

It gets a little more interesting as we starting looking at realtime data push. I’ve been interested in WebSocket, but support is spotty. Then I found Pusher, which is awesome. It’s simple, and it just works. Pusher provides a JavaScript library, but it has been ported to a bunch of other languages.

On the client-side I used AIR.

Before we jump in to the nuts-and-bolts I’ll make my disclaimer, this isn’t meant to showcase clean code or a clean user experience, it’s a spike to prove it all works.

So pulling it all together. The Node.js app is deployed to Heroku and uses the Pusher add-on to send realtime messages. The AIR client connects to Pusher and makes a JSON-RPC call to the Node.js app. The AIR client receives a response back from Node.js and a message from Pusher.

Deploying the app

  • Clone the server-side code from GitHub
git@github.com:p15martin/BlogPusherServer.git
  • If you don’t already have an account on Heroku then create one and install the command line client (CLI)
  • Move to your command prompt and change directory to where you cloned the server-side code
  • Create a new app on Heroku:
heroku create --stack cedar
  • Take a note of the app that was created (e.g. empty-night-2166) and the remote repository (e.g. git@heroku.com:empty-night-2166.git)
  • Add your Heroku app as a remote repository (remember to change it to your remote repository):
git remote add heroku git@heroku.com:empty-night-2166.git
heroku addons:add pusher:sandbox
  • In your browser, log on to Heroku and select your app (e.g. empty-night-2166)
  • From the add-ons menu select Pusher
  • Select the development app

  • Select API access

  • Take a note of the Pusher.app_id, the Pusher.key and the Pusher.secret
  • Now to change the code, in an editor open PusherServer/web.js
  • At the top of the file update the appId, the key, and the secret with what you got from Heroku, save the file
  • At the command prompt commit your changes:
git commit -a -m "updating pusher details"
  • Push your changes to Heroku:
git push heroku master

  • Now test your app (assuming you have curl installed), remember to change the url to your Heroku app:
curl -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "add", "params": [1,2], "id":2 }' http://empty-night-2166.herokuapp.com
  • If it worked then you should receive this response (if not jump down to the troubleshooting section): {“result”:3,”id”:2,”jsonrpc”:”2.0″}
  • Run the PusherClient by either installing the client, or by cloning the project from GitHub and importing it in to FlashBuilder
git@github.com:p15martin/BlogPusherClient.git
  • In the client enter your Pusher key and update the Heroku url to point to your app
  • Click Connect to Pusher and then click Send JSON-RPC message to Node
  • The first time may be a little slow, but you should see the JSON-RPC request and response, and a message from Pusher

The client code

The code is raw, but it should be simple enough to follow. In the client code (PusherClient.mxml) the key functions are connect() and sendMessage().

In the connect() function I am using the Pusher-ActionScript-Library from Shawn Makison. It’s kinda simple. You connect to Pusher:

var pusher : Pusher = new Pusher( pusherKey.text, "http://experiencecraftsmanship.com");

You then subscribe to the channel, which is “test_channel” in my example:

var channel : Channel = pusher.subscribe("test_channel");

Finally, you listen for events on your channel, in my example I am listening for “my_event“:

channel.bind( "my_event", eventCallback );

The AS3 library is semantically similar to the Pusher JavaScript library, so if you want to go further then take a look at the JavaScript quick start guide and the JavaScript client API guide. Generally speaking the Pusher docs are a good source of reference.

The sendMessage() function is a little more involved. We start by constructing our JSON-RPC request. I am using the native JSON support added in FlashPlayer 11. So I create an Object that represents my request and then call JSON.stringify(), which takes the Object and returns it in JSON format.

The final part is to send the request to the server, which is just a case of ensuring our HTTP request is set up correctly.

It’s not rocket science and the code could be easily refactored to separate out the different responsibilities and to create a clean API that abstracts the developer from the detail.

private function sendMessage() : void
{
   var object : Object = new Object();
   object.jsonrpc = "2.0";
   object.method = "add";
   object.params = [1, 2];
   object.id = 2;

   var message : String = JSON.stringify( object );

   jsonRequest.text = jsonRequest.text + message + "\n";

   var httpService:HTTPService = new HTTPService();
   httpService.method = "POST";
   httpService.url = herokuUrl.text;
   httpService.contentType = "application/json";
   httpService.headers = { Accept:"application/json" };
   httpService.resultFormat = HTTPService.RESULT_FORMAT_TEXT;

   var token : AsyncToken = httpService.send(message);
   token.addResponder( new mx.rpc.Responder( handleResult, handleFault ) );
}

The server code

I used a few packages, namely the node-jsonrpc package from Eric Florenzano, and node-pusher from Jaewoong Kim. In addition to these packages being referenced in the code (web.js), they are also specified to Heroku in package.json.

If you look at their respective examples you will see I have simply combined them in to my spike.

In the deployment section I called out the Pusher API details that you must update. I would also draw your attention to the channel and event, which need to match what are in the client code:

var channel = 'test_channel';
var event = 'my_event';

The final mention is to the add() function where I send the message to Pusher:

pusher.trigger(channel, event, message, null, function(err, req, res) {
   console.log("pushed");
});

In terms of gotchas the main thing to watch it the port. Make sure you bind the server to the port that Heroku sets in the environment:

var port = process.env.PORT || 3000;
express.createServer(
   require('connect-jsonrpc')(math, date)
).listen(port);

Although Heroku dynamically assigns the port number, you still connect using port 80 (I got caught out by that one).

Troubleshooting

If you run in to trouble then check the logs. You can configure the logging level from the command line as follows:

heroku config:add LOG_LEVEL=DEBUG

To check the logs use:

heroku logs

Other than getting tripped up by the port, which I mentioned above, I didn’t run in to any specific issues. It just worked.

Additional useful resources

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s