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).

Comments (19)

  1. Ferdinand says:

    Great post, very helpful! There's a typo in the second header: "Impementing APIs". 😉

  2. Thanks, fixed that typo! 🙂

  3. Matt Milner says:

    Carlos, do you know when the HTML/JS client library will be updated to support these APIs?  Is there a new version already that can be referenced from our HTML apps?  

    Thanks for the great information!

  4. Matt, the HTML/JS should be updated sometime this week. I'll post an update to this post (or to the next post specifically about the client SDKs) when this happens.

    Thanks!

  5. Ignacio Fuentes says:

    Hey Carlos, this is looking better every day.

    When can we expect the updated sdks to show up on github?

    The xamarin sdks are starting to really lag behind.

  6. Maxence Lerigner says:

    Hi Carlos,

    Great update! Thank you so much for these new features.

    About the sharing code part…

    I'm not sure I got this right: except in the custom api, where can we use the code in ../shared ?

    I'm trying to 'require' my shared code in my schedule scripts and my table scripts without success.

    Is this part of the next update or am I doing something wrong?

    Thanks!

  7. @Matt, the HTML/JS SDK should be updated now.

    @Ignacio, I don't know when the Xamarin SDKs will be updated, but I should post something when this happens.

    @Maxence, there's currently a bug for using npm modules within table / scheduler / feedback scripts, this should be fixed over the next couple of weeks.

  8. Cedric says:

    Thank you Carlos, this is exactly what I was looking for.

    Just one problem, I have problems accessing values in req.body (I use POST routing).

    I can see that req.body is containing JSON but when I try to access a variable, I get an undefined value in the console.

    Do you have any idea ?

    Thanks

  9. Gonzalo says:

    Can we use external nodejs modules, such as crypto?

  10. Neto says:

    Carlos, How can I do to use external nodejs modules, such as cheerio?

  11. @Cedric, try 'console.log' the value of req.body, and (typeof req.body). If it's a string, then you need to parse it (using JSON.parse) to make it an actual object. If the request content-type is 'application/json', it should be parsed for you, though, please let us know if this is not the case.

  12. @Gonzalo / @Neto, yes, you can use external node.js modules – anything that NPM supports. You can do that by enabling source control, and from there you can 'npm install' any module you want. The tutorial at http://www.windowsazure.com/…/store-scripts-in-source-control shows how this can be done.

  13. Harald says:

    you mentioned that a bug with using such shared code in scheduler and table scripts will be fixed in the next couple of weeks, any update on that 'cause it still seems to be an issue – or I do something wrong…

  14. @Harald, this is working. The easiest way is to enable source control in your service (http://www.windowsazure.com/…/store-scripts-in-source-control), and store the shared code in the 'shared' folder. I just tried it and it worked fine. For example, for this file (a.js) in the shared folder:

       exports.a = { foo: 'bar', bar: 123 };

    And this read script in a table on the service:

       function read(query, user, request) {

           var a = require('../shared/a');

           request.respond(200, a);

       }

    If you send a GET request to that table (your-service.azure-mobile.net/tables/table-name), you'll get the result from the shared file.

  15. Harald says:

    thx for the reply. Meanwhile I figured out what I was doing wrong too.

    Cloning my mobile service locally with git showed that the custom api is stored in another directory and that was the problem. I had misunderstood a howto I found about that.

    Thx anyway!

  16. Alexander says:

    Hi! This is awesome, and I've been using it for a while.

    I would like to set different permissions for different registered routes though (i.e User for "GET /" and Admin for "GET /adminstuff").

    That's not possible, so I thought I'd create a little middleware that checks request.user.level. But (as is clear from the source) it's not possible to pass an array as you would normally do in Express. Is there any good way to do it?

    Thanks! (awesome post; wouldn't have known about register otherwise)

  17. roaned says:

    Good day, I implemented exports.get = function(request,response) of a custom api on a mobile service of azure. I download 5 thousands records from the database and then i prepare the json for the output. The problem is that the time of downloading of all records is too long, for that my script goes into timeout. I was thinking if there is a way to increase the timeout of the response.

  18. @roaned, that's not a good practice in node.js world. Remember that node has only a single thread, and if you spend a lot of time preparing the JSON output, that thread will be blocked and no other requests will be able to reach your service. You should consider splitting your function to return a subset of your records, and some sort of token that can be passed to the same API so that you can get the next subset of the records in your complete response.

  19. roaned says:

    @CarlosFigueira ok thanks, then I make this solution

Skip to main content