Building Back-end Data and Services for Windows 8 Apps: Windows Azure Mobile Services

If you’ve been following along with this series so far, you know we’ve come along way, from my overview of the series, which described the target scenario of a simple leaderboard service for a couple of JavaScript-based Windows 8 games, to the 2-part post (Part 1 / Part 2) on building this service using WCF Data Services, and hosting as an Azure Cloud Service, to the most recent installment, in which I showed how to build the same service using ASP.NET Web API, and hosted in a Windows Azure Web Site.

To round out the group of solutions I’m exploring in the series, in this post I’ll show you how to build the same game leaderboard service on top of the new Windows Azure Mobile Services (in preview as of this writing).

A Different Approach

In both of the previous versions of the leaderboard service, the solution involved a code-based server-side solution. On the plus side, in both cases, Visual Studio did most of the work of creating the code (along with the Entity Framework, WCF Data Services, and Web API libraries), but the end product is a project with source code that needs to be maintained.

By contrast, Windows Azure Mobile Services takes a simpler approach, that allows me as the developer to focus more on my client code, and less on the server side code.

Getting Started

As with my other examples, you’ll need a Windows Azure account to follow along. If you have an MSDN Subscription, check your benefits page, as it may include some Windows Azure benefits. If you don’t have an MSDN subscription, you can sign up for a 90-day trial that will give you everything you need to follow along, and by default includes a spending cap to ensure that you don’t have any out-of-pocket expenses if your services exceed the trial service limits (if your services exceed the trial limits, they’ll automatically be shut down).

With my Azure account in-hand, I start by signing into the Windows Azure Management Portal, which by default gives me an overview of all of the services I have running in Windows Azure, which includes all subscriptions that are managed by the account I use to log in, as you can see below:

ManageAllItems_4

One of the “tabs” along the left side is MOBILE SERVICES, which is just what I’m looking for, so I’ll click that, which takes me to the mobile services home page. I’ve removed a couple of existing services I was working with, so there’s not much to see yet:

MobileServices1_2

I’ll get started by clicking the + NEW link at the bottom (clicking the link in the middle of the page would work, too), which gives me a nice Create option below (and shows neatly where Mobile Services fits in the Azure ecosystem):

MobileServices2_New_2

Clicking Create gives me the following 2-step wizard for creating the service (since I already created a database instance in an earlier part of this series, I’ll go ahead and leverage that existing database instance):

MobileServices3_Create1_2

Note that the URL must be unique, and the wizard will check the name dynamically and let you know if your desired name is available. For the free version of Mobile Services, all URLs will use the pattern MYSERVICENAME.azure-mobile.net, but I can (at an additional cost) also configure my service to use a custom domain name, once the initial setup is complete. For my purposes, the existing domain name is fine, since end-users won’t be interacting with the service directly.

Clicking the arrow icon takes me to the second page of the wizard, where I will provide my database credentials:

MobileServices3_Create2_4

Clicking the checkmark icon will start the process of creating the service, which takes a few minutes. When the service has been set up, I get a screen that looks like the following:

MobileServices4_Created_2

NOTE: Please be aware that while the service URL above is public, I may or may not be keeping it active after this post is complete, so it may not be accessible. If you want to experiment with mobile services, I encourage you to create your own service.

What Have I Done?

OK, so now I have my mobile service. But what does that mean, exactly?

Basically, in a few short steps, I have (with the help of Azure) built out an infrastructure that will provide me with:

  • A place to store data
    • The ability to create tables and schemas (schemas are dynamic by default…more on that later)
      • RESTful services to access the data in any tables I create
        • Client libraries to simplify the process of interacting with the back-end data tables
          • Push notification services

            • Authentication providers
            I’ll cover the last two features in future posts, but the rest of what we get is pretty cool. Essentially, when I create a new table (I’ll walk through how I do that shortly), Windows Azure Mobile Services automatically creates RESTful services that provide basic Create, Read, Update, and Delete functionality for that table. And while mobile services also provides client libraries for several client platforms (with more on the way), the underlying services are accessible via simple HTTP calls, so they’re open to any client platform that can make HTTP calls.

          And for folks who like transparency, the client SDK for Mobile Services is being developed on GitHub, so you can check out the code, fork it, and contribute bug fixes and features as well.

          Getting a Quick Start

          Now that my mobile service is created, I can simply click the name of the service to start working with it. By default, the first screen I’ll see is the Quick Start page, shown below (note the checkbox, which allows me to skip the Quick Start next time):

          MobileServices5_2

          The Quick Start screen is designed to get users up and running fast with their mobile services, and (as of this writing) supports 3 client platforms out of the box: Windows Store (C# or JavaScript apps), Windows Phone 8, and iOS. Android support has been announced as well.

          UPDATE: As of March 2013, support has been added for both Android clients, as well as HTML clients, making Mobile Services an even better solution, no matter what type of client app you’re building.

          For each of these client platforms, the Quick Start page provides a tutorial on creating a new app for that platform using the new mobile service, or alternatively, for connecting an existing app to the mobile service. Since my goal is to connect this service to a couple of existing games, that’s what I’ll start with.

          Note also that the Quick Start page provides links to instructions for adding authentication and push notifications for each platform (again, I’ll show you how to leverage these features in future posts).

          Since I’m going to use my service with a Windows Store app, I’ll switch to the Windows Store plaftorm, then click the heading labeled “Connect an Existing Windows Store app”, which gives me some simple instructions, as shown below:

          MobileServices6_2

          The first two steps are pretty straightforward, one, installing the Mobile Services SDK, and two, referencing the SDK in my Windows Store app project, and adding the initialization code to the project. The initialization code in step 2 provides the client library with the URL from which to access the service, plus the application key (obfuscated above), which is the default means of authenticating the app to the service and authorizing table access. The Data tab for a given service in the management portal allows more restrictive permissions to be put in place, but I’m going to stick with using the application key for the time being.

          I’ve already got the Mobile Services SDK installed, so let me show you how simple it is to add a reference to the SDK. I’ll open up my Windows Store app (in this case, the Space Cadet game I’ve been using as one of my demo apps throughout this series), and in Solution Explorer, right-click the References node (which you’ll find right below the project node), and select Add Reference… Find the Windows Azure Mobile Services JavaScript Client, and check the checkbox to the left of it, as shown below:

          AddReference_2

          Then click OK to add the reference.

          Step 3 in the Quick Start shows the syntax needed to add new items to an Item table, which I can create by clicking the Create Item table button. Alternately, if I want a table with my own name, I can go to the Data tab and add a new table there:

          MobileServices7_DataTab_2

          When I click the Add a Table link (or the Create link at the bottom of the page), I’m presented with a dialog that allows me to specify the name of the table, as well as the permissions for the Insert, Update, Delete, and Read permissions:

          MobileServices8_CreateTable_2

          As noted earlier, the default is to allow anyone with the application key to access these operations on the table. Additional permissions include Everyone, which opens the operation up to anyone who has the service URL (you might use this to provide open access to the read operation, but it would be pretty rare to use this for other operations), Only Authenticated Users, and Only Scripts and Admins. The latter permission means that the table can only be accessed in the management portal and via server-side scripts.

          I’ll set my table name to “gamescore,” as shown above, and click the checkmark to create the table. Once the table creation is complete, if I click into the gamescore table, and switch to the Columns tab, I see that there’s only one column defined, called id, and it’s indexed, and of type bigint.

          One thing you might notice is that there’s no interface here to add columns. That’s because by default, mobile services use a dynamic schema model, in which anything I add to the named table from my client code will automatically generate the necessary schema on the fly. This provides a very flexible mechanism for storing data, particularly if you don’t know up-front what data you’ll need to store.

          Connecting the Client

          At this point, I have everything I need on the server side to successfully connect my game to the leaderboard service. The next step is to wire up the client code, and as with the previous examples, I’ll do this by creating a separate JavaScript file that contains all the logic for interacting with the service, and expose this via a defined API using the WinJS.Namespace.define function, just as I did in the previous examples in this series. Let’s take a look at the full code, and I’ll explain the key parts one by one:

          leaderboardWAMS.js

             1:  (function () {
   2:      "use strict";
   3:   
   4:      // Leaderboard Client for Windows Azure Mobile Services
   5:      // by G. Andrew Duthie
   6:      // This code is copyright © Microsoft Corporation, and licensed under the Microsoft Limited Public License (Ms-LPL). 
   7:      // All rights reserved. 
   8:      // Code is provided AS-IS with no warranties.
   9:   
  10:      var leaderboardClient, gameScoresTable, leaderboardList, playerName, gameName, diag;
  11:   
  12:      WinJS.Namespace.define("Leaderboard", {
  13:          setPlayerName: setPlayerName,
  14:          init: init,
  15:          addWin: incrementWins,
  16:          addLoss: incrementLosses,
  17:          addTie: incrementTies,
  18:          updateScore: updateHighScore,
  19:          getTopTenScores: getTopTenScores,
  20:          getTopTenWins: getTopTenWinLossTie,
  21:          leaderboardList: leaderboardList
  22:      });
  23:   
  24:      // player and game are required for initialization
  25:      function init(player, game) {
  26:          return new WinJS.Promise(
  27:              function (completed, error, progress) {
  28:                  if (player) {
  29:                      playerName = player;
  30:                  }
  31:                  else {
  32:                      error("Player Name is required for initialization");
  33:                  }
  34:   
  35:                  if (game) {
  36:                      gameName = game;
  37:                  }
  38:                  else {
  39:                      error("Game Name is required for initialization");
  40:                  }
  41:   
  42:                  // replace with your custom WAMS instance information
  43:                  var leaderboardClient = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient(
  44:                      "https://YOUR_UNIQUE_SERVICE_NAME.azure-mobile.net/",
  45:                      "YOUR_INSTANCE_KEY"
  46:                  );
  47:                  // Replace "gamescore" with your table name, if different
  48:                  gameScoresTable = leaderboardClient.getTable('gamescore');
  49:                  // make sure that the score table has at least one record for the player name / game name combo
  50:                  gameScoresTable.where({ player: playerName, game: gameName })
  51:                      .read().done(
  52:                      function (results) {
  53:                          if (results.length == 0) {
  54:                              // initialize table
  55:                              var gameScore = {
  56:                                  game: gameName, player: playerName, score: 0, wins: 0, losses: 0, ties: 0
  57:                              };
  58:                              gameScoresTable.insert(gameScore);
  59:                          };
  60:                          completed();
  61:                      },
  62:                      function (e) {
  63:                          showMessage(e.message);
  64:                      });
  65:              });
  66:      }
  67:   
  68:   
  69:      function getCurrentPlayerScore() {
  70:          return new WinJS.Promise(
  71:              function (completed, error, progress) {
  72:                  gameScoresTable.where({ player: playerName, game: gameName })
  73:                  .read().done(
  74:                  function (results) {
  75:                      completed(results[0]);
  76:                  },
  77:                  function (e) {
  78:                      // handle exceptions
  79:                      showMessage(e.message);
  80:                  });
  81:              });
  82:      }
  83:   
  84:      function setPlayerName(name) {
  85:          var namePromise = getCurrentPlayerScore();
  86:          namePromise.done(function (currentScore) {
  87:              if (playerName != name) {
  88:                  playerName = name;
  89:                  currentScore.player = playerName;
  90:                  gameScoresTable.update(currentScore);
  91:              }
  92:          }, function (e) {
  93:              showMessage(e.message);
  94:          });
  95:      }
  96:   
  97:      function incrementWins() {
  98:          var scorePromise = getCurrentPlayerScore();
  99:          scorePromise.done(function (currentScore) {
 100:              currentScore.wins++;
 101:              gameScoresTable.update(currentScore);
 102:          }, function (e) {
 103:              showMessage(e.message);
 104:          });
 105:      }
 106:   
 107:      function incrementLosses() {
 108:          var scorePromise = getCurrentPlayerScore();
 109:          scorePromise.done(function (currentScore) {
 110:              currentScore.losses++;
 111:              gameScoresTable.update(currentScore);
 112:          }, function (e) {
 113:              showMessage(e.message);
 114:          });
 115:      }
 116:   
 117:      function incrementTies() {
 118:          var scorePromise = getCurrentPlayerScore();
 119:          scorePromise.done(function (currentScore) {
 120:              currentScore.ties++;
 121:              gameScoresTable.update(currentScore);
 122:          }, function (e) {
 123:              showMessage(e.message);
 124:          });
 125:      }
 126:   
 127:      function updateHighScore(newScore) {
 128:          var scorePromise = getCurrentPlayerScore();
 129:          scorePromise.done(function (currentScore) {
 130:              if (currentScore.score <= newScore) {
 131:                  currentScore.score = newScore;
 132:                  gameScoresTable.update(currentScore).done(function () {
 133:                      showMessage("Leaderboard Updated.");
 134:                  });
 135:              }
 136:          }, function (e) {
 137:              showMessage(e.message);
 138:          });
 139:      }
 140:   
 141:      function getTopTenScores() {
 142:          return new WinJS.Promise(
 143:              function (completed, error, progress) {
 144:                  gameScoresTable
 145:                  .select("player", "score")
 146:                  .where({ game: gameName })
 147:                  .orderByDescending("score")
 148:                  .read().done(
 149:                      function (results) {
 150:                          leaderboardList = new WinJS.Binding.List(results);
 151:                          completed(leaderboardList);
 152:                      },
 153:                  function (e) {
 154:                      // handle exceptions
 155:                      error(e);
 156:                  });
 157:              });
 158:      }
 159:   
 160:      function getTopTenWinLossTie() {
 161:          return new WinJS.Promise(
 162:              function (completed, error, progress) {
 163:                  gameScoresTable
 164:                  .select("player", "wins", "losses", "ties")
 165:                  .where({ game: gameName })
 166:                  .orderByDescending("wins")
 167:                  .read().done(
 168:                      function (results) {
 169:                          leaderboardList = new WinJS.Binding.List(results);
 170:                          completed(leaderboardList);
 171:                      },
 172:                  function (e) {
 173:                      // handle exceptions
 174:                      error(e);
 175:                  });
 176:              });
 177:      }
 178:   
 179:      function showMessage(msg) {
 180:          diag = new Windows.UI.Popups.MessageDialog(msg);
 181:          diag.showAsync();
 182:      }
 183:  })();

Here are the important parts of the code:

I also need to reference the MobileServicesJavaScriptClient/MobileServices.js file as well (note that this reference should be BEFORE the reference to my script above):

<script src="/MobileServicesJavaScriptClient/MobileServices.js"></script>

In the case of the leaderboard page, I also need to make sure that the databinding properties in my template match the column names in my table, as these are case-sensitive (lines 4 and 7 below):

   1:  <div id="myTemplate" data-win-control="WinJS.Binding.Template">
   2:      <div>
   3:          <div class="win-type-x-large" style="width: 400px;">
   4:              <span>Player: </span><em><span data-win-bind="textContent: player"></span></em>
   5:          </div>
   6:          <div class="win-type-large">
   7:              <span>Score: </span><em><span data-win-bind="textContent: score"></span></em>
   8:          </div>
   9:      </div>
  10:  </div>

Because the functions exposed in my Leaderboard namespace are the same as in the previous examples, there’s no need to update the game logic at all. Once I’ve updated the script reference, I can simply run the game, and when the game ends, if my score is higher than the existing score, I’ll see the following:

screenshot_12052012_211304_4

And when I visit the leaderboard page, here’s what I see:

screenshot_12132012_180458_2

Of course, since I’ve only just created my table, there’s only one score record, but it’s nice to see that it’s working fine.

For good measure, after updating the code for the Catapult Wars game, and playing a couple of games, here’s what my gamescore table data looks like in the management portal:

gamescore_final_2

Wrap-up

In this post, I’ve shown you how easy it is to get started with Windows Azure Mobile Services. Creating a new mobile service and table is fast and simple, and the client libraries provide a quick and intuitive way of wiring your apps up to your mobile service, without worrying about the underlying REST calls.

In fact, simply by switching my leaderboard service over to Windows Azure Mobile Services, I was able to reduce the size of my leaderboard JavaScript library by nearly 30%. That’s a significant reduction in code, nearly all of which is due to the simpler code required thanks to the client library.

But the cool part is that it’s all still just RESTful services under the covers. So if I was building an app on a platform for which Windows Azure Mobile Services does not yet provide a client library (for example, Android, or even a web site), as long as that platform can make HTTP calls, I can still leverage mobile services as a back-end. You can read all about the Windows Azure Mobile Services REST API here.

Here are the advantages and disadvantages, in my view, of using Windows Azure Mobile Services for this kind of service:

Advantages