5. Build REST API client like a BOSS (Almog Baku, Co-Founder & CTO @ Rimoto)
We all love to write REST applications with AngularJS, but what is the proper way to handle RESTful API?
In this talk, we'll discuss about solutions for interacting with RESTful APIs in AngularJS - tips, tricks, and all the secrets of doom.
1. Build REST API client like a BOSS
Build RESTful API clients for AngularJS, the proper way
2. Who are you?
@AlmogBaku nice to meet ya`
1. Entrepreneur
2. Co-Founder & CTO @ Rimoto
3. Developer for 10 years
4. GitHub addicted.
5. Blog about entrepreneurship and development:
www.AlmogBaku.com
3. What are we going to talk about?
● What tha’ heck is REST?
● Why $resource is sucks?
● $http
● Say hi to Restangular
● Do it like a boss
10. REST
REST is just the regular way the internet works!
GET http://google.com/
11. REST
REST is just the regular way the internet works!
GET http://google.com/RESPONSE 200 OK
12. REST
REST is about resources, not about functions.
Book store api:
1. /api/authors/
2. /api/authors/:authorId/
3. /api/authors/:authorId/books/
4. /api/authors/:authorId/books/:bookId
5. /api/authors/:authorId/books/:bookId/reviews
6. /api/authors/:authorId/books/:bookId/reviews/:reviewId
13. REST
REST is about resources, not about functions.
Book store api:
1. /api/authors/
2. /api/authors/:authorId/
3. /api/authors/:authorId/books/
4. /api/authors/:authorId/books/:bookId
5. /api/authors/:authorId/books/:bookId/reviews
6. /api/authors/:authorId/books/:bookId/reviews/:reviewId
15. REST
REST is about resources, not about functions.
Book store api:
1. /api/authors/
2. /api/authors/:authorId/
3. /api/authors/:authorId/books/
4. /api/authors/:authorId/books/:bookId
5. /api/authors/:authorId/books/:bookId/reviews
6. /api/authors/:authorId/books/:bookId/reviews/:reviewId
17. REST
The same URIs can do many different actions...
We can request web pages in one of the following methods:
1. GET - request information about resource
2. POST - create new resource
3. PUT - update resource
4. DELETE - delete resource
5. HEAD - get information only with headers (eg. if resource exists)
6. OPTIONS - list of available methods to the resource (like --help)
18. REST
Errors are simple http errors
200 - OK
404 - Not found
401 - Unauthorized
500 - Server Error
Etc.
19. REST
REST is Stateless
- You can’t use cookies
- You need to pass your identification in every request
GET /users/me?access_token=ftjhi89uh5982hbrvt92vgt9qvhg2r0219
23. The thing is…
It just sucks
1. It can be very complex...
var Author = $resource('https://api.rimoto.net/api/v1/author/:authorId', {authorId:'@id'});
Author.get({authorId:183}, function(author) {
author.name = 'J.R.R. Tolkien';
author.$save();
});
var Book = $resource('https://.../api/v1/author/:authorId/books/:bookId', {authorId: 183, bookId:'@id'});
Book.get({bookrId:2}, function(book) {
book.name = 'Lord of the rings';
book.$save();
});
24. The thing is…
It just sucks
2. Doesn’t allow you to parse the API
3. No way to set base url application wide
4. You can’t handle errors application wide
5. You can’t handle headers application wide
6. You can’t handle anything application wide
Actually- $resource is okay for anything other than serious apps.
25. What can we do?
We can use $http, in order to add necessary headers:
We can also use the $http interceptor, in order to handle errors...
var req = {
method: 'POST',
url: 'http://example.com',
headers: {
'Content-Type': 'application/json'
},
data: { test: 'test' }
};
$http(req).success(function(){...}).error(function(){...});
26. What can we do?
We can also use the $http interceptor, in order to handle errors...
$provide.factory('myHttpInterceptor', function($q, dependency1) {
return {
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
'response': function(response) {
//parsing here....
return response;
},
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
27. What can we do?
We can also use the $http interceptor, in order to handle errors...
$provide.factory('myHttpInterceptor', function($q, dependency1) {
return {
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
'response': function(response) {
//parsing here....
return response;
},
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
28. What can we do?
We can also use the $http interceptor, in order to handle errors...
$provide.factory('myHttpInterceptor', function($q, dependency1) {
return {
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
'response': function(response) {
//parsing here....
return response;
},
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
29. What can we do?
We can also use the $http interceptor, in order to handle errors...
$provide.factory('myHttpInterceptor', function($q, dependency1) {
return {
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
'response': function(response) {
//parsing here....
return response;
},
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
32. Restangular
1. Restangular solves ‘em all!
2. Published on 2013
3. Very stable and popular angular-module
4. Active community
5. Allows you to configure your REST API in application level
6. Restangular 2 will support AngularJS 2! (WIP)
7. Very easy to use
34. Say hi to Restangular
Let’s create a new author:
var Authors = Restangular.all('authors');
Authors.post({
name: "J.R.R. Tolkien"
});
35. Say hi to Restangular
Lets’ create a new author:
var Authors = Restangular.all('authors');
Authors.post({
name: "J.R.R. Tolkien"
});
POST
http://api.rimoto.net/api/v1/authors/
36. Say hi to Restangular
Lets’ create a new author:
var Authors = Restangular.all('authors');
Authors.post({
name: "J.R.R. Tolkien"
});
37. Say hi to Restangular
Get the author:
Restangular.one('authors', 183).get()
.then(function(author) {
$scope.author = author;
})
;
38. Say hi to Restangular
Get the author:
Restangular.one('authors', 183).get()
.then(function(author) {
$scope.author = author;
})
;
GET
http://api.rimoto.net/api/v1/authors/183
39. Say hi to Restangular
Get all his books:
$scope.author.getList('books')
.then(function(books) {
var firstBook = books[0];
firstBook.name="The Hobbit";
firstBook.put();
})
;
46. Model parsing
How can we solve that?
1. Create a transformer
2. Add the function to the model!
3. Fin.
47. Model parsing
How can we solve that?
1. Create a transformer
2. Add the model this function!
3. Fin.
RestangularConfigurer.ExtendModel("review", function(review) {
review.voteUp = function() {
return review.getAll("votes").post({
vote_up: true
});
};
return review;
});
48. Model parsing
We can do use this tool for many cases:
1. Ordering lists
2. Special parsing
3. Add functions
4. Etc.
51. PRO tips
Bundle your whole API as “SDK”, and extend the Restangular to be your own
API service:
module
.factory('API', ['Restangular',
function(Restangular) {
angular.extend(this, Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl("https://api.rimoto.net");
RestangularConfigurer.setRequestSuffix('/');
RestangularConfigurer.setDefaultHeaders({ Accept: 'application/json;v=1' });
}));
return this;
}])
;
52. PRO tips
1. You can set the `id` parameter (eg. for mongodb):
RestangularProvider.setRestangularFields({
id: '_id'
});
53. PRO tips
2. Handle the Access-Tokens on application level
Example for Restangular with ngAuth:
/** Login changed **/
$rootScope.$on("Auth.status", function(event, response) {
if(response.access_token) {
Restangular.setDefaultRequestParams({
access_token: response.access_token
});
} else {
Restangular.setDefaultRequestParams({
access_token: null
});
}
});
54. PRO tips
3. Decouple Restangular to models
4. You can define Restangular to use HATEOAS
5. Keep your API service generic, and handle specific use-cases separately.
module.factory('Users', function(Restangular) {
return Restangular.service('users');
});
RestangularProvider.setRestangularFields({
selfLink: 'self.link'
});