Custom APIs in Azure Mobile Services

Today Scott Guthrie announced a couple of new features in Azure Mobile Services: custom APIs and Git source control. The Git source control is a preview feature, and I’ll talk about it in a future post, but custom APIs are ready to be used. Scott’s post already talked a little about this feature, but I want to go in a little more depth over this post.

Creating APIs

As mentioned in the original post, creating APIs via the new API tab:

01-CreateAPI

APIs can be used for many different scenarios, but for simplicity sake, I’ll go back to the calculator which I’ve used often in this blog. My calculator will respond to two different kinds of requests: GET and POST, and I want anyone to be able to use my new service, so I’ll set the permission for those two verbs to ‘Everyone’.

02-NewApiAndPermissions

Since my implementation will only expose the GET and POST verbs, the permissions for the other verbs don’t really matter, but as a matter of security in depth I prefer to leave all the verbs which I don’t plan on exposing with the permission set to ‘Administrator’, so that if I expose the other operations in my script by mistake they won’t be exposed to the general public.

Once you click the ‘ok’ button to create the API, you can get click into it and see that it already contains a script file which responds to the POST verb.

Implementing APIs

Custom APIs in Azure Mobile Services are implemented as node.js modules (but, as Scott mentioned in his post, support for .NET implementations is coming in the future), and the requests they respond to are defined as exports to the API module. As can be seen in the default implementation of the API, it already responds to the POST verb, with a simple ‘Hello world’ response.

  1. exports.post = function (request, response) {
  2.     // Use "request.service" to access features of your mobile service, e.g.:
  3.     // var tables = request.service.tables;
  4.     // var push = request.service.push;
  5.  
  6.     response.send(200, "Hello World");
  7. };

The request object contains all the information you need to know about the incoming HTTP request. The object is actually the same object you’d get if you were using the express node module (it’s the module used by the Azure Mobile Service runtime), with some additional objects: the ‘service’ object, which lets you access the Azure Mobile Services-specific objects which were on the global scope in table scripts, such as ‘tables’ and ‘push’; and the ‘user’ object, which is equivalent to the user object passed to the table scripts. For example, this API responds to the POST verb saying whether the user is authenticated or not:

  1. exports.post = function (request, response) {
  2.     var result = {
  3.         isAnonymous: request.user.level === 'anonymous'
  4.     };
  5.     response.send(200, result)
  6. };

Besides those two properties, the request object has all of the HTTP-specific properties (inherited from the express request object) listed on the express documentation for its request object.

So let’s implement the GET handler. To do that, we need to export a get function. For now, let’s start with a simple implementation with all the parameters being passed via the query string.

  1. exports.get = function (req, res) {
  2.     var x = parseInt(req.query.x, 10);
  3.     var y = parseInt(req.query.y, 10);
  4.     var operation = req.query.op;
  5.     var result;
  6.     if (operation === 'add') {
  7.         result = x + y;
  8.     } elseif (operation == 'sub') {
  9.         result = x - y;
  10.     } else {
  11.         res.send(400, { error: 'Operation "' + operation + '" not supported' });
  12.     }
  13.  
  14.     res.send(200, { result: result });
  15. };

So with this export, we can now make a call to https://YOUR_SERVICE.azure-mobile.net/api/calculator?op=add&x=8&y=13 and we’ll get the result. For the POST operation, we can do something similar, receiving the parameters in the request body:

  1. exports.post = function (request, response) {
  2.     var x = request.body.x;
  3.     var y = request.body.y;
  4.     var operation = request.body.operation;
  5.     var result;
  6.     if (operation === 'add') {
  7.         result = x + y;
  8.     } elseif (operation == 'sub') {
  9.         result = x - y;
  10.     } else {
  11.         response.send(400, { error: 'Operation "' + operation + '" not supported' });
  12.     }
  13.  
  14.     response.send(200, { result: result });
  15. };

Or even better, using a common function for both:

  1. exports.post = function (request, response) {
  2.     var x = request.body.x;
  3.     var y = request.body.y;
  4.     var operation = request.body.operation;
  5.     calculateAndRespond(x, y, operation, response);
  6. };
  7.  
  8. exports.get = function (req, res) {
  9.     var x = parseInt(req.query.x, 10);
  10.     var y = parseInt(req.query.y, 10);
  11.     var operation = req.query.op;
  12.     calculateAndRespond(x, y, operation, res);
  13. };
  14.  
  15. function calculateAndRespond(x, y, op, res) {
  16.     var result;
  17.     if (op === 'add') {
  18.         result = x + y;
  19.     } elseif (op == 'sub') {
  20.         result = x - y;
  21.     } else {
  22.         res.send(400, { error: 'Operation "' + op + '" not supported' });
  23.     }
  24.  
  25.     res.send(200, { result: result });
  26. }

But that’s not exactly the URL schema which I want for my calculator. I actually want my operation to be part of the request URL, with the requests being sent to service.azure-mobile.net/api/calculator/operation. When I first tried this, my idea was to use the ‘path’ request property to get the operation from there, as in the code below:

 

  1. exports.get = function (req, res) {
  2.     var x = parseInt(req.query.x, 10);
  3.     var y = parseInt(req.query.y, 10);
  4.     var path = req.path;
  5.     var operation = path.substring('/api/calculator/'.length);
  6.     calculateAndRespond(x, y, operation, res);
  7. };

However, when I sent a GET request to myservice.azure-mobile.net/api/calculator/add?x=6&y=9, I got a 404 response back. First lesson I learned about the “simple” export mechanism: the handlers only respond to requests to the exact path /api/<apiName>, not to its “sub-paths”.

Routing

To support a scenario where we want requests to go not only to /api/<apiName>, but to other paths under this as well, we need to use the routing feature from express. To use the routing feature, we need a different export, called ‘register’. That function will be passed the application (app) object from express, and from there you can register the routes as functions taking request and response objects.

  1. exports.register = function (api) {
  2.     api.get('*', getImplementation);
  3. };
  4.  
  5. function getImplementation(req, res) {
  6.     var x = parseInt(req.query.x, 10);
  7.     var y = parseInt(req.query.y, 10);
  8.     var path = req.path;
  9.     var operation = path.substring('/api/calculator/'.length);
  10.     calculateAndRespond(x, y, operation, res);
  11. }

Notice that inside the ‘register’ export you aren’t limited to registering routes; you can also change application settings as well.

But now that we’re already using routes, we can also take advantage of express’ route parameters feature, in which our route can define some parameters (captures over the URL) which we can access directly in the implementation, without needing to do any URI manipulation ourselves. With that, we can rewrite our calculator with that. And by adding a few more operations, we have our final implementation of our calculator.

  1. exports.register = function (api) {
  2.     api.get('/:op', getImplementation);
  3.     api.post('/:op', postImplementation)
  4. };
  5.  
  6. function getImplementation(req, res) {
  7.     var x = parseInt(req.query.x, 10);
  8.     var y = parseInt(req.query.y, 10);
  9.     var operation = req.params.op;
  10.     calculateAndRespond(x, y, operation, res);
  11. }
  12.  
  13. function postImplementation(req, res) {
  14.     var x = req.body.x;
  15.     var y = req.body.y;
  16.     var operation = req.params.op;
  17.     calculateAndRespond(x, y, operation, res);
  18. }
  19.  
  20. function calculateAndRespond(x, y, op, res) {
  21.     var operations = {
  22.         add: function (x, y) { return x + y; },
  23.         sub: function (x, y) { return x - y; },
  24.         mul: function (x, y) { return x * y; },
  25.         div: function (x, y) { return x / y; }
  26.     };
  27.     if (operations[op]) {
  28.         res.send(200, { result: operations[op](x, y) });
  29.     } else {
  30.         res.send(400, { error: 'Operation "' + op + '" not supported' });
  31.     }
  32. }

Now all is good – the ‘calculator’ API listens to the GET and POST verbs, and all code here is self-contained.

Sharing code

But now we need to implement the same calculator logic in another API. This is a feature that is frequently asked for table scripts, and it will be implemented shortly. For APIs, however, it’s already working, so let’s show how this can be done (if you’re using the Git source control you can implement it in another way, but I’ll leave it for a future post; here I’ll describe how to do it via the portal only).

As I mentioned before, a custom API is implemented as a node.js module. It’s common practice in node to group related functionality in modules, and export the functionality that the module wants to expose to external users. In the custom API modules, we can export operations corresponding to HTTP verbs (or the special ‘register’ function) which are then picked up (via ‘require’) by the Azure Mobile Services runtime to hook it up to the service. But nothing prevents other APIs in the same service from ‘require’-ing other API modules and using them. So in some projects where I want to have some shared code, I just create a new custom API (called ‘shared’, for convention, but any name would do), and, since I don’t intend on exposing it directly to the external world, I use the “administrator” permission for all the HTTP verbs:

03-sharedApi

Now, on that API, I can add our logic as one of the exports – as this is the only code in the script for the ‘shared’ API. For testing purposes, I’ll make the subtraction operation return an incorrect result, to make sure that we’re hitting it correctly.

  1. exports.operations = {
  2.     add: function (x, y) { return x + y; },
  3.     sub: function (x, y) { return 1000 + x - y; },
  4.     mul: function (x, y) { return x * y; },
  5.     div: function (x, y) { return x / y; }
  6. };

And we can change the implementation of our calculator API to use our shared code:

  1. function calculateAndRespond(x, y, op, res) {
  2.     var operations = require('./shared').operations;
  3.     if (operations[op]) {
  4.         res.send(200, { result: operations[op](x, y) });
  5.     } else {
  6.         res.send(400, { error: 'Operation "' + op + '" not supported' });
  7.     }
  8. }

Now, send a GET request to YOUR_SERVICE.azure-mobile.net/api/calculator/sub?x=100&y=10, and you’ll get the (incorrect by design) result of 1090.

Just a final note: there is currently a bug in which sometimes updates to shared code aren’t picked up immediately by the runtime (it should be fixed over the next week or two). If this happens, you can try restarting the service (I find the easiest way to do that is to go to the “configure” tab and toggle the “dynamic schema” feature, then toggle it back). As I said, this should be fixed over the next couple of weeks.

Wrapping up

As usual, please try the new feature and let us know what you think. Over the next few days I’ll have another post how to invoke the custom APIs on the different client SDKs. The support for that is on the latest version of the SDKs from the azure downloads page (except the Android SDK, which should be updated over the next few days).