Angular JS: Using ngResource in a more RESTful manner

27 Jun 2013

A key problem I found (due to how I like to work) is that the $save method on ngResource will only ever issue POST requests to the server, along with the payload. This is true for both create and update operations - aka, both new and old objects from our server. This breaks RESTful conventions as update operations should be done with PUT or PATCH. My proposed solution below extends the current ngResource implementation, and provides more defaults, whilst streamlining our workflow. The perfect use-case (imho) should be like below:

1 var user = new User;
2 user.name = 'Kirk Bushell';
3 user.$save(); // POST
4 
5 var user = User.get( { id: 1 });
6 user.name = 'John smith';
7 user.$save(); // PUT

If we dig into the ngResource code - such a requirement is impossible, due to how the simple the implementation is (much to the Angular team’s credit). Unfortunately, it does mean that if we wanted to support both POST/PUT for saves, we’d have to use two separate methods (not my style). In my humble opinion, a save is a save - let our module/library define what kind of save it is. What we need to do is extend the $resource factory that ngResource provides, with our own defaults. Let’s look at that now.

 1 var module = angular.module( 'my.resource', [ 'ngResource' ] );
 2 
 3 module.factory( 'Resource', [ '$resource', function( $resource ) {
 4   return function( url, params, methods ) {
 5     var defaults = {
 6       update: { method: 'put', isArray: false },
 7       create: { method: 'post' }
 8     };
 9     
10     methods = angular.extend( defaults, methods );
11 
12     var resource = $resource( url, params, methods );
13 
14     resource.prototype.$save = function() {
15       if ( !this.id ) {
16         return this.$create();
17       }
18       else {
19         return this.$update();
20       }
21     };
22 
23     return resource;
24   };
25 }]);

Update: Resolved the save method not returning results - thanks bpetering!

Here we’re defining a custom module - my.resource. This module can then be injected into other modules that want this extended functionality. We’re then injecting $resource as a dependency for our own Resource factory, and doing a little magic. Let’s investigate.

First, we define our new defaults. This is both an update, and a create method for the resource - with create being defined as a POST request, and the update method being defined as a PUT request. Why do we want these two extra methods? Because it allows us to do specific requests, if we should so need - but it’s also so we can overload the $save method!

Below this we’re extending the defaults with any methods you may have provided to the resource itself. Then, we’re defining our new resource and extending this by overloading the $save method. This method checks to see if an id has been provided as part of the resource object, and if it has, it will make a call to the $update method we defined in our defaults. If not, it will call the $create method. Very simple!

But - how do we then use this in our own resources? Piece of cake.

1 var module = angular.module( 'services', [ 'my.resource' ] );
2 
3 module.factory( 'User', [ 'Resource', function( $resource ) {
4   return $resource( 'users/:id', { id: '@id' } );
5 }]);

As you can see - we’re treating it just like any other resource, the only difference - is that we’re defining our $resource dependency as Resource - which is our extension of ngResource that we defined above.

It’s really that simple. Now you can make your resource object calls like normal! As always, feedback, criticism, and suggestions are always welcome.

Update: Thanks to Maikel Daloo for the angular.extend suggestion!

comments powered by Disqus