12

My question is that given the power of interceptors, does it make sense to wrap $http in a service so that all my other code just calls that wrapper. Now basic tasks like header/exception handling can easily be done by interceptors. So though I cant think of a valid usecase now, but lets say just to shield any future api changes etc to $http? Or maybe later migrate to $resource?

Also please note that here I am talking of a basic wrapper service around $http’s methods, not a client service like DataService with methods sendData, receiveData that wraps $http calls. Please find below sample code -

angular.module(‘myapp’).factory(‘myhttpwrapper’, ['$http', function (http) {
  return {

    myGet: function (getUrl) {
      return http.get(getUrl);
    },
    myPost: function (postUrl, data) {
      return http.post(postUrl, data);
    },
    // other $http wrappers
  };
}]);

Now all other code will use myhttpwrapper’s myGet, myPost methods instead of $http’s get, post methods. Hope it makes sense!

[EDIT] What use-cases we'll definitely have is to intercept requests for adding headers, logging, and responses for logging, exception handling, etc. But I am sure these can be handled by interceptors. Later moving from $http to $resource is not known at this point.

Thanks.

3
  • 2
    What would you gain in using a service that has exactly the same API as $http? I don't see any advantage in doing this. It'll only confuse developers. Commented Jan 25, 2014 at 13:28
  • Yes, even I don't think there is much use, but just wanted to check from community for best practice. Commented Jan 25, 2014 at 15:46
  • I would go against wrapping existing service just for maintenance perspective. People would think myhttpwrapper is not $http unless they look at your code carefully. That is waste of time. Commented Jan 25, 2014 at 19:36

2 Answers 2

15

For the specific case you describe, I'd advise against wrapping $http. There is no real gain in doing it.

A case where this could come into play would be where you'd want a more 'speaking' API. Lets say you have User(s) in a system, and Address(es). You described it as a data based service DataService:

var app = angular.module("users", []);

app.service("User", ['$http', '$q', function(http, q) {
  return {
    getAddress: function(user) {
      var address = q.defer();
      http.get("/user/" + user.id + "/address").then(function(data) {
        address.resolve(data);
      }, function(err) {
        address.reject(err);
      });
      return address.promise;
    },
    getUser: function() {
      var user = = q.defer();
      http.get("/user/address").then(function(data) {
        user.resolve(data);
      }, function(err) {
        user.reject(err);
      });
      return user.promise;
    }
  }
}]);

This allows you to use parameters for your call. Whenever you'd have to change routes, you would have only one place to change them (this would be really awful if, say, you had a dozen controllers making $http requests).

You can also make use of $resource here if you which (and you actually have compatible resources). The decision making factor(s) here should be readability, reusability and ease of change later.

So, if you only ever have on place where you make these calls and the routes never change, go ahead and use the $http directly, without a wrapper. This I'd consider unlikely in a growing application.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Florian. So we'll have lots of such DataServices (ex. Users as above) in our app and we'll wrap all $http calls in them to shield the service clients from url etc changes.
@Florian makes a great point here. I typically add another layer and wrap http within another service which stores all the "magic" urls elsewhere, that way a particular service isn't tied to a set of set urls. Then you can swap urls with no problem. It may be overkill but I have found having one file with urls separate from my entity definitions keeps my backend devs from screwing up my entity logic ;-)
1

I needed my 'get' generalized so did it like this:

app.factory('myHttp', ['$http', '$q', function($http, $q) {
return {
    get: function(url) {
        var deferred =  $q.defer();
        $http.get(url).then(
            function(response) {
                //console.log("response good");
                deferred.resolve(response)
            },
            function(response) {
                //console.log("response bad");
                deferred.reject(response)
            })
        return deferred.promise;
    }
};
}]);

You can now chain an additional "then" to the result of the get. You can have core logic for handling error or success (my use case being to log out the user if credentials expired) but still allow for additional context specific logic for the get response (like setting $scope etc...).

1 Comment

Note that your use of return deferred.promise; can be simplified. You can just use return $http.get(url).then( .... response functions .... ). Then, in the response functions, you can return $q.when(response);. More on this: codelord.net/2015/09/24/$q-dot-defer-youre-doing-it-wrong

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.