Azure Mobile Services with AngularJS $resource

Microsoft released the ability to create custom APIs with Azure Mobile Services in June 2013 and released the JavaScript client for Azure Mobile Services (AMS) even earlier in March 2013. The JavaScript client is excellent and easy to use, but it doesn’t play well with AngularJS. Actually, that’s not true. It plays just fine with AngularJS but it requires you to use $scope.$apply() when you’re working with the data retrieved.

The thing is, Azure Mobile Services, even the custom API feature, is just REST. There’s no reason we can’t use it with $resource and get all that awesome data manipulation for free. All it requires is that we call AMS ourselves, without using the client library.

####In this post, we’ll see how to connect to a custom API in Azure Mobile Services using the AngularJS $resource feature.

The first step is to create the Custom API. I’m not really going to go into how to create a custom API in AMS. Check out this tutorial for a walkthrough of setting up a custom API endpoint. I’ve named my endpoint commentsapi and here’s what my simple API backend code looks like.

exports.get = function(request, response) {
    
    var comments = [
        {
            id: 1,
            commentDate: '2014-10-14 07:38 PM',
            commentText: 'I love commenting'
        },
        {
            id: 2,
            commentDate: '2014-10-15 06:14 AM',
            commentText: 'Comments are just the best'
        },
    )    {
            id: 3,
            commentDate: '2014-10-16 2:26 PM',
            commentText: 'We love mangoes'
        }
        
    ];
    
    response.send(statusCodes.OK, comments);
};

Pretty impressive, eh? It’s just sending back some static JSON data on the default GET request. In your super awesome application, you’ll probably have something a little more useful in your API. But this works for now.

Here’s the structure of the data that I want to return (for a single item):

{
    commentDate: '2014-10-14 04:38 PM',
    commentText: 'This is my comment.',
    id: 4
}

##Reference Point: Client Library for JavaScript Let’s start with a baseline of what calling an API using the Microsoft provided client library for JavaScript looks like. Here’s an AngularJS service I created that will abstract the AMS logic:

app.factory('CommentsService', function() {
    var service = {
        query: function(success, error) {
            var client = new WindowsAzure.MobileServiceClient(
                "https://yourservice.azure-mobile.net/",
                "API KEY");            

            client.invokeApi('commentsapi', {
                method: 'GET'
            }).done(function (response) {
                success(JSON.parse(response));
            });
        }
    };

    return service;
});

To use this service, I’ve got a controller that will do the remainder of the work. Here is my ideal controller code:

apiresourcedemo.controller('ApiDemoCtrl', ['$scope', 'CommentsService',
    function($scope, CommentsService) {
        $scope.comments = CommentsService.query();
    }]);

But here’s the rub: if you just do this, it won’t work. Well, the network call will work, but nothing will end up in the $scope. You’ll be left with an empty scope and fewer hairs on your head after pulling them in frustration.

To make it work, you’d need to do this in the controller:

apiresourcedemo.controller('ApiDemoCtrl', ['$scope', 'CommentsService',
    function($scope, CommentsService) {
        CommentsService.query(function(results) {
            $scope.$apply(function() {
                $scope.comments = results;
            })
        })
    }]);

If you’re used to dealing with callbacks in JavaScript, this probably doesn’t look that bad to you. But even with the callback, there is this $scope.$apply() going on making everything kind of gross. Here’s what the AngularJS documentation says about $scope.$apply():

$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

Okay. So what? Who cares? Well, it’s just unnecessary.

##Improved Version Using $resource

We can just as easily call the AMS API using $resource to eliminate all that extra junk. Here’s what a refactored service looks like using $resource:

apiresourcedemo.factory('CommentsService', ['$resource', function($resource) {
    var service = $resource("https://yourservice.azure-mobile.net/api/commentsapi", null, 
        {
            'query': {
                method: 'GET',
                isArray: true,
                headers: GetApiHeaders()
            }
        });

    return service;
}]);

function GetApiHeaders() {
    var headers = {
        'X-ZUMO-APPLICATION': "API KEY"
    };

    return headers;
}

If you’ve never seen the $resource syntax before, it’s a little intimidating, but it’s really not bad and I don’t think it looks much worse than the client library version. You can read the AngularJS documentation on $resource for all the grungy details. Now, my ideal controller can exist just like I wanted:

apiresourcedemo.controller('ApiDemoCtrl', ['$scope', 'CommentsService',
    function($scope, CommentsService) {
        $scope.comments = CommentsService.query();
    }]);

Because $resource uses promises, there are no callbacks to deal with and, in a triumph only a geek could appreciate, there’s no more $scope.$apply(). I can hear you saying, “Hey, you just replaced some gross code for some other gross code!” I suppose you could say that, but to my eye, the $resource version is cleaner. For one thing, our concerns are separated better since the controller doesn’t have to know what the service is going to send in the callback. Plus, if you’re like me, you’re already using AngularJS, so eliminating the AMS client library reduces the size of your app payload and improves performance.

Of course, you can use $resource for POST, PUT, and DELETE, but I’ll leave that to you. Lest you think this code is just a trivial example, the code I’ve shown here is the basis for what runs the comments service on vndr. While I’ve shown how to do this for custom API endpoints on Azure Mobile Services, the same techniques apply to calling the table endpoints.

Music for this post: No Doubt - Tragic Kingdom

You should follow me on Twitter