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:

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

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.

exports.post = function (request, response) {

// Use "request.service" to access features of your mobile service, e.g.:

// var tables = request.service.tables;

// var push = request.service.push;

response.send(200, "Hello World");

};

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:

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:

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:

exports.get = function (req, res) {

var x = parseInt(req.query.x, 10);

var y = parseInt(req.query.y, 10);

var path = req.path;

var operation = path.substring('/api/calculator/'.length);

calculateAndRespond(x, y, operation, res);

};

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.

exports.register = function (api) {

api.get('*', getImplementation);

};

function getImplementation(req, res) {

var x = parseInt(req.query.x, 10);

var y = parseInt(req.query.y, 10);

var path = req.path;

var operation = path.substring('/api/calculator/'.length);

calculateAndRespond(x, y, operation, res);

}

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.

exports.register = function (api) {

api.get('/:op', getImplementation);

api.post('/:op', postImplementation)

};

function getImplementation(req, res) {

var x = parseInt(req.query.x, 10);

var y = parseInt(req.query.y, 10);

var operation = req.params.op;

calculateAndRespond(x, y, operation, res);

}

function postImplementation(req, res) {

var x = req.body.x;

var y = req.body.y;

var operation = req.params.op;

calculateAndRespond(x, y, operation, res);

}

function calculateAndRespond(x, y, op, res) {

var operations = {

add: function (x, y) { return x + y; },

sub: function (x, y) { return x - y; },

mul: function (x, y) { return x * y; },

div: function (x, y) { return x / y; }

};

if (operations[op]) {

res.send(200, { result: operations[op](x, y) });

} else {

res.send(400, { error: 'Operation "' + op + '" not supported' });

}

}

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:

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.

exports.operations = {

add: function (x, y) { return x + y; },

sub: function (x, y) { return 1000 + x - y; },

mul: function (x, y) { return x * y; },

div: function (x, y) { return x / y; }

};

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

function calculateAndRespond(x, y, op, res) {

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

if (operations[op]) {

res.send(200, { result: operations[op](x, y) });

} else {

res.send(400, { error: 'Operation "' + op + '" not supported' });

}

}

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

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

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…

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?

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.

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