build rest api clients for angularjs

Post on 18-Jul-2015

326 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Build REST API client like a BOSSBuild RESTful API clients for AngularJS, the proper way

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

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

Disclaimer

You wanna know more? Google it!

What tha’ heck is REST?

Representational State Transfer

Watttt??

What is API?

API is a layer that connects two applications.

Application Programing Interface

What is API?

API is actually a common language, that

both of the parties knows.

REST

REST is a Client-Server API

REST

REST is just the regular way the internet works!

GET http://google.com/

REST

REST is just the regular way the internet works!

GET http://google.com/RESPONSE 200 OK

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/:bookId5. /api/authors/:authorId/books/:bookId/reviews6. /api/authors/:authorId/books/:bookId/reviews/:reviewId

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/:bookId5. /api/authors/:authorId/books/:bookId/reviews6. /api/authors/:authorId/books/:bookId/reviews/:reviewId

REST

GET /api/authors/

[ { "id": 7, "name": "J.R.R. Tolkien", "birthday": "1-3-1892" }, { "id": 183, "name": "Douglas Adams", "birthday": "3-11-1952" }]

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/:bookId5. /api/authors/:authorId/books/:bookId/reviews6. /api/authors/:authorId/books/:bookId/reviews/:reviewId

REST

GET /api/authors/187/

{ "id": 183, "name": "J.R.R. Tolkien", "full_name": "John Ronald Reuel Tolkien", "birthday": "1-3-1892", "genre": "SciFi"}

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)

REST

Errors are simple http errors

200 - OK

404 - Not found

401 - Unauthorized

500 - Server Error

Etc.

REST

REST is Stateless

- You can’t use cookies

- You need to pass your identification in every request

GET /users/me?access_token=ftjhi89uh5982hbrvt92vgt9qvhg2r0219

REST API+

REST with AngularJS

It’s pretty simple actually.. just use $resource

var Author = $resource('/api/v1/author/:authorId', {authorId:'@id'});Author.get({authorId:183}, function(author) { author.name = 'J.R.R. Tolkien'; author.$save();});

The thing is…It just sucks

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();});

The thing is…It just sucks

2. Doesn’t allow you to parse the API3. No way to set base url application wide4. You can’t handle errors application wide5. You can’t handle headers application wide6. You can’t handle anything application wide

Actually- $resource is okay for anything other than serious apps.

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(){...});

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');

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');

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');

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');

Say hi to

Restangular

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

Configurations

Define initial settings:

Defining base url:

Defining Content-Type headers:

RestangularProvider.setBaseUrl('http://api.rimoto.net/api/v1/');

RestangularConfigurer.setDefaultHeaders({'Content-Type': 'application/json'});

Say hi to Restangular

Let’s create a new author:

var Authors = Restangular.all('authors');Authors.post({ name: "J.R.R. Tolkien"});

Say hi to Restangular

Lets’ create a new author:

var Authors = Restangular.all('authors');Authors.post({ name: "J.R.R. Tolkien"});

POSThttp://api.rimoto.net/api/v1/authors/

Say hi to Restangular

Lets’ create a new author:

var Authors = Restangular.all('authors');Authors.post({ name: "J.R.R. Tolkien"});

Say hi to Restangular

Get the author:

Restangular.one('authors', 183).get() .then(function(author) { $scope.author = author; });

Say hi to Restangular

Get the author:

Restangular.one('authors', 183).get() .then(function(author) { $scope.author = author; });

GEThttp://api.rimoto.net/api/v1/authors/183

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(); });

Pretty simple.. huh?

Model parsing

RestangularConfigurer.ExtendModel("books", function(book) { book.published = new Date(book.published); return book;});

We can “transform” data from our API to javascript!

Model parsing

<div class="vote-box"> <button ng-click="voteUp($review)" class="up"><i class="up-arrow"></i></button> <div>{{votes|number}}</div> <button ng-click="voteDown($review)" class="down"><i class="down-arrow"></i></button></div>

Model parsing

<div class="vote-box"> <button ng-click="voteUp($review)" class="up"><i class="up-arrow"></i></button> <div>{{votes|number}}</div> <button ng-click="voteDown($review)" class="down"><i class="down-arrow"></i></button></div>

Vote done on the controller

Model function

But it’s an API function!

Model parsing

How can we solve that?

Model parsing

How can we solve that?

1. Create a transformer2. Add the function to the model!3. Fin.

Model parsing

How can we solve that?

1. Create a transformer2. 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;});

Model parsing

We can do use this tool for many cases:

1. Ordering lists2. Special parsing3. Add functions4. Etc.

What about errors?

RestangularConfigurer.setErrorInterceptor(function(response) { if([401, 403, 405].indexOf(response.status)!=-1) { logout(); return false; }});

Build REST client like a BOSS

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; }]);

PRO tips

1. You can set the `id` parameter (eg. for mongodb):

RestangularProvider.setRestangularFields({ id: '_id'});

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 }); }});

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'});

Check it out

github.com/mgonto/restangular

Be creative,and create your own API

Questions?

Thanks.

top related