Blog

Search This Blog

Loading...

Thursday, May 1, 2014

Going (Almost) Serverless with Iron.io

The NoisyTwit App
This is a guest post by Dieter Van der Stock, a full-stack developer in Antwerp, Belgium. In it, he talks about his experience building NoisyTwit and how the combination of HTML/JavaScript, PHP, OAuth.io, and Iron.io made for a simple but scalable solution. 

Modern app development doesn't need to be complicated as this post details.



Building NoisyTwit in a Few Easy Steps


A few weeks ago I set out on a project I've been wanting to make for a while now. The idea is both silly and simple – I wanted to know who the noisiest people in my Twitter feed were. Usually you know who these are, obviously, but with accounts that retweet a lot it can be difficult to estimate.

The idea for the app was more of an excuse though. What I really wanted to do was create a back-endless web application. A service that performed some logic, without me having to actually run any server.

I'm a back-end engineer by trait, so it's not that I'm afraid of servers, or Apache configs or anything like that. But a server is nonetheless a moving part in your stack and I wanted to get rid of them wherever possible. The less moving parts there are in your stack, the easier the maintenance and the lower the chance is you'll have to be called in to fix things.

Being familiar with the Iron.io stack, I figured IronWorker could do the 'heavy lifting', that is, get the tweets, analyze them, and produce results. The front-end could be pure HTML/Javascript and hosted on Amazon S3 which means no moving parts needed for the front-end either.

The OAuth communication with Twitter was the last piece of the puzzle, and that's where OAuth.io came in. It's a great library that handles the complex OAuth details by acting as a proxy. All you need to do is call their JavaScript methods and you're pretty much good to go.

(Before we go too far, yes, "serverless" may be somewhat of a misnomer because servers are still needed. It's just that by using cloud services, I don't have to mess with them. Somebody else does and that's fine by me.)


The Application Flow

For every user trying the service, a number of things need to happen in sequence. Here's what I came up with for the event handling:
  1. On the front-end: let the user authorize NoisyTwit to read their Twitter information
  2. Push the access keys we receive after authorization to an IronWorker (via a webhook)
  3. In the worker: get the tweets from their timeline and analyze them (count the times any user showed up in the timeline)
  4. Push the results to an IronCache slot
  5. Again in the front-end: show the results to the user
Since the front-end can't know when the work is done, it polls IronCache periodically and only renders the results once there are any. An AJAX spinner is shown to let the user know that something is actually happening.



Twitter Authorization / Cache Key Agreement

Asking for Twitting Authorization
As mentioned, getting permission from the user to their Twitter stream is done via OAuth.io. The return of the call to OAuth.io provides us with the access tokens we'll need to talk to Twitter on the user's behalf.

We'll send these tokens to the task we've uploaded on IronWorker, but also we'll also do one additional thing beforehand. We want to decide ahead of time what key the worker will use to store the results in IronCache when it's done. This way the front-end knows where to poll for results.

This results in code like this:

  var webhookurl = 'https://worker-aws-us-east-1.iron.io/2/projects/<projectid>/tasks/webhook?code_name=noisytwitworker&oauth=<youroauthkey>';
  var data = {};
  data.access_token = access_token;
  data.access_token_secret = access_token_secret;
  data.cachekey = cachekey;

  $.ajax({
      url: webhookurl,
      type: 'post',
      data: JSON.stringify(data),
      dataType: 'json',
      success: function (data) {
          // redirect user to results page (where polling for results happens)
          window.location.href = "findthenoise.html";
      },
      error: function (data) {
          // Show the user an error has happened (use the big button)
          setButtonToWorkerError();
      }
  });


Kicking of the Worker for Tweet Retrieval and Analysis

Awaiting the Results
When the webhook URL is called in the code above, our beloved IronWorker kicks in. The data we sent with the webhook is made available as a payload, which is automatically json_decoded for you. When you're in PHP, like I am in this case, you can just run getPayload() to get to the data.

An important tip: while Iron automatically performs json_decode(), it doesn't decode it to an array. The payload will be an object. So instead of typing $payload["access_token"], you want $payload->access_token.

The worker will query Twitter, receive the tweets, and count how many times each tweeter in present in the feed. Once this is done, the result is written to the agreed upon IronCache key, which has been included in the worker payload.

Pushing the result to IronCache at the agreed-upon key looks like this:

$cachekey = $payload->cachekey;
$ironCache = new IronCache(array(
    'token'         =>  <iron_project_token>,
    'project_id'    =>  <iron_project_id>
));
$ironCache->setCacheName(<iron_cache_name>);
$ironCache->put($cachekey, $results);

You can talk to IronCache from PHP by including the IronCache library when you upload the worker. If you're wondering how to get anything but the worker itself up to the Iron.io cloud, it's actually quite simple. In my case I made a composer.json file, included what I needed in there, and ran composer install locally on my machine.

The composer file for my worker is as follows:

{
    "require": {
        "php": ">=5.3",
        "iron-io/iron_cache": "dev-master",
        "ricardoper/twitteroauth": "dev-master"
    }
}

This will give you a vendor directory which contains all the libraries you wanted. Then you can just go to your .worker file, and include the /vendor directory for upload.

My .worker file looks like this:

runtime "php"
exec "noisytwitworker.php"
dir "vendor"

Waiting for the Results in the Front-end

The job that the worker is doing can take a few seconds, so the front-end must know when that job is complete. As highlighted earlier, the front-end will poll IronCache on the agreed-upon key until a result is displayed. (I know, I know, polling is an anti-pattern. One could use a websocket but I wanted as few moving parts as possible, remember?)

My poll function in the frontend looks like this:

var pollForResults = function() {
  $.ajax({
    url: cacheUrl,
    statusCode: {
      404: function() {
          setTimeout(function() { pollForResults(); }, 15000);
      }
    },
    success: function(xhr_data) {
      whenResultsAreIn(xhr_data);
    }
  });
}

Once the results have arrived, I use the Handlebars.js templating library to render the results:

Displaying the Results

Wrapping Up

NoisyTwit was a fun project to experiment with IronWorker and IronCache in the context of static/back-endless web apps. Iron.io's stack gives us quite a few new options when designing our architecture. It made a pretty significant change in that regard for me personally.

I'm hoping that this post can help some people out who are still working on connecting the dots, as I was a few months back. If there is anything that's not yet cleared up, don't hesitate to ask me on Twitter or talk to the great guys at Iron.io themselves, of course.




About the Author
Dieter Van der Stock is a full stack software engineer working on a number of freelance projects. His tools include Node.js, PHP (Laravel/Symfony), AWS, and Iron.io.

Get in touch with Dieter on Twitter at @dietervds!



To learn more about how IronWorker can provide your app with an scalable compute cloud, please visit Iron.io today.