Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
ANGULARJS
BACKEND
STORIES
How to build a backend-ready app
WITHOUT BACKEND
Wearing multiple hats at Narada Robotics startup, from coding to whatever else…
Main interests - mobile development
IN LOV...
SCENARIO
I want you to develop my frontend
I’ll provide you with an API REST
SCENARIO
I want you to develop my frontend
I’ll provide you with an API REST
BACKEND STORIES 5
URLS TO LOCAL JSONS
Of course we can use urls to local JSON files, but…
We cannot use POST, PUT...
We ca...
WHAT CAN WE DO?
MOCKING THE SERVER
What we will cover:
URL DEFINITIONS01
CREATING JSON FIXTURES02
USING $HTTPBACKEND03
USING $RESOURCE04
D...
BACKEND STORIES 8
CAUTION
This is NOT intended to be used in PRODUCTION
@enrique.oriol
BACKEND STORIES 9
Why not Node.js mock?
It is just an alternative to any other mock server
It runs at the same JS VM, so i...
BACKEND STORIES 10
You can download code here:
https://github.com/kaikcreator/UserRanking
And play with the results here:
...
QUICK OVERVIEW USER RANKING
Require Following Integration With API
NON AUTHENTICATED
CREATE USER - POST user endpoint
USER...
URL DEFINITION
Let’s organize our code
BACKEND STORIES 13
URL DEFINITION
app/services/url/URLModule.js
angular.module('userRanking.urlsModule', [])
.constant('ur...
BACKEND STORIES 14
URL DEFINITION
app/services/url/URLModule.js
.factory('urls', ['urlConst', function(urlConst){
var urls...
BACKEND STORIES 15
URL DEFINITION
This Approach
EASY TO EXTEND
WITH MORE
ENDPOINTS
ISOLATES URL
DETAILS FROM
OTHER SERVICE...
CREATING JSON
FIXTURES
We should agree in the models
BACKEND STORIES 17
CREATING JSON FIXTURES
How a user will look like
{
id: userId,
name: username,
image: urlToUserImage,
r...
BACKEND STORIES 18
CREATING JSON FIXTURES
app/fixtures/users.json
[
{
"id": "1",
"name": "Peter",
"image": "user1.png",
"p...
E2E
$HttpBackend
As a mechanism not to test, but to mock our server
BACKEND STORIES 20
e2e $httpBackend
https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend
1. download
Installation...
BACKEND STORIES 21
e2e $httpBackend
https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend
When URL: return object ...
BACKEND STORIES 22
e2e $httpBackend
app/services/mock/MockServerModule.js
angular.module('userRanking.mockServerModule', [...
$resource
As a mechanism to load local JSON files
BACKEND STORIES 24
$resource
https://docs.angularjs.org/api/ngResource/service/$resource
1. download
Installation
$ bower ...
BACKEND STORIES 25
$resource: return object with different methods, that populates async
Usage
$resource(url, [paramDefaul...
BACKEND STORIES 26
$resource
app/services/mock/MockServerModule.js
angular.module('userRanking.mockServerModule', ['ngMock...
BACKEND STORIES 27
If you perform a $http call just after app boostrap, it’s possible that $resource is
not already popula...
Dynamic
responses
The power behind mocking server with E2E
$httpBackend
BACKEND STORIES 29
Dynamic responses
Using function inside $httpBackend.respond
<!-- mocked data -->
$httpBackend.whenGET(...
BACKEND STORIES 30
Dynamic responses
We can perform data validation, and return custom headers
.run(['$httpBackend', 'urls...
BACKEND STORIES 31
Dynamic responses
We lack persistence
.run(['$httpBackend', 'urls', 'fixturePaths', '$resource', functi...
Backend persistence
Completing the experience with localStorage
BACKEND STORIES 33
Write in LS
$window.localStorage['myKey'] = angular.toJson(myData);
Read from LS
var data = angular.fro...
BACKEND STORIES 34
.constant('lsMock', {
"users": "ls-cached-users"
})
.factory('mockedUsers', ['$window', '$resource', 'f...
BACKEND STORIES 35
.run(['$httpBackend', '$resource', 'urls', 'mockedUsers', function($httpBackend, $resource, urls, mocke...
BACKEND STORIES 36
/* mock update user*/
$httpBackend.whenPUT(anyUserUrl).respond(
function(method, url, data, headers){
d...
BACKEND STORIES 37
/* mock new user*/
$httpBackend.whenPOST(anyUserUrl).respond(
function(method, url, data, headers){
dat...
You’re ready
Time to play with code
http://kaikcreator.github.io/UserRanking/
THANK YOU!
http://lnkdin.me/enriqueoriol
blog.enriqueoriol.com
@enrique.oriol
Nächste SlideShare
Wird geladen in …5
×

How to build an AngularJS backend-ready app WITHOUT BACKEND

1.058 Aufrufe

Veröffentlicht am

These slides show an approach on how to build a backend ready AngularJS app, when the backend is not yet available, using the tools AngularJS provide for E2E testing.

Of course, this is something that can be achieved also with a Node.js (or any other language) mock server, but it has the advantage that just by running your angularJS app, the mock server is also running, so in the case of a hybrid app, for example, you can send an APK to somebody to try your app (with mocked data), without the need to deploy your mock server to set it available on the cloud.

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

How to build an AngularJS backend-ready app WITHOUT BACKEND

  1. 1. ANGULARJS BACKEND STORIES How to build a backend-ready app WITHOUT BACKEND
  2. 2. Wearing multiple hats at Narada Robotics startup, from coding to whatever else… Main interests - mobile development IN LOVE WITH ANGULARJS Enrique Oriol CTO @ Narada Robotics blog.enriqueoriol.com @enrique.oriol
  3. 3. SCENARIO I want you to develop my frontend I’ll provide you with an API REST
  4. 4. SCENARIO I want you to develop my frontend I’ll provide you with an API REST
  5. 5. BACKEND STORIES 5 URLS TO LOCAL JSONS Of course we can use urls to local JSON files, but… We cannot use POST, PUT... We cannot use response codes We cannot access headers So we cannot really prepare client side API connections @enrique.oriol
  6. 6. WHAT CAN WE DO?
  7. 7. MOCKING THE SERVER What we will cover: URL DEFINITIONS01 CREATING JSON FIXTURES02 USING $HTTPBACKEND03 USING $RESOURCE04 DYNAMIC RESPONSES05 BACKEND PERSISTENCE06
  8. 8. BACKEND STORIES 8 CAUTION This is NOT intended to be used in PRODUCTION @enrique.oriol
  9. 9. BACKEND STORIES 9 Why not Node.js mock? It is just an alternative to any other mock server It runs at the same JS VM, so it does not need a public endpoint somewhere Think about sending the results to a client in the case of a hybrid app @enrique.oriol
  10. 10. BACKEND STORIES 10 You can download code here: https://github.com/kaikcreator/UserRanking And play with the results here: http://kaikcreator.github.io/UserRanking/ EXAMPLE APP We will use User Ranking app as example @enrique.oriol
  11. 11. QUICK OVERVIEW USER RANKING Require Following Integration With API NON AUTHENTICATED CREATE USER - POST user endpoint USERS RANKING - GET users endpoint AUTHENTICATED UPDATE USER - PUT user endpoint
  12. 12. URL DEFINITION Let’s organize our code
  13. 13. BACKEND STORIES 13 URL DEFINITION app/services/url/URLModule.js angular.module('userRanking.urlsModule', []) .constant('urlConst', (function(){ var protocol = 'http://'; var domain = 'www.userRanking.com'; var base = '/api'; var version = '/v1'; var placeholders = { userId: '[userId]' } return { user: protocol + domain + base + version + '/user/' + placeholders.userId, users: protocol + domain + base + version + '/users', placeholders: placeholders }; })()) @enrique.oriol
  14. 14. BACKEND STORIES 14 URL DEFINITION app/services/url/URLModule.js .factory('urls', ['urlConst', function(urlConst){ var urls = angular.copy(urlConst); var replacePlaceholders = function(url, placeholders){ for(var key in placeholders){ url = url.replace(urlConst.placeholders[key], placeholders[key]); } return url; }; //generate dinamic urls here urls.user = function(id){ return replacePlaceholders(urlConst.user, {userId: id}); }; return urls; }]); @enrique.oriol
  15. 15. BACKEND STORIES 15 URL DEFINITION This Approach EASY TO EXTEND WITH MORE ENDPOINTS ISOLATES URL DETAILS FROM OTHER SERVICES @enrique.oriol
  16. 16. CREATING JSON FIXTURES We should agree in the models
  17. 17. BACKEND STORIES 17 CREATING JSON FIXTURES How a user will look like { id: userId, name: username, image: urlToUserImage, rating: userRating } @enrique.oriol
  18. 18. BACKEND STORIES 18 CREATING JSON FIXTURES app/fixtures/users.json [ { "id": "1", "name": "Peter", "image": "user1.png", "points": 150 }, . . . { "id": "6", "name": "Travis", "image": "user6.png", "points": 100 } ] @enrique.oriol
  19. 19. E2E $HttpBackend As a mechanism not to test, but to mock our server
  20. 20. BACKEND STORIES 20 e2e $httpBackend https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend 1. download Installation $ bower install angular-mocks 2. include source <!-- mock server --> <script src="bower_components/angular-mocks/angular-mocks.js"></script> 3. include module angular.module('app', ['ngMockE2E']); @enrique.oriol
  21. 21. BACKEND STORIES 21 e2e $httpBackend https://docs.angularjs.org/api/ngMockE2E/service/$httpBackend When URL: return object with responds & passthrough methods Basics when( method, url, [data], [headers], [keys] ); Responds Passthrough $httpBackend.when(‘GET’, url).respond({} / []) $httpBackend.when(‘GET’, url).respond(function(method, url, data, headers, params){}) $httpBackend.when(‘GET’, url).passThrough() @enrique.oriol
  22. 22. BACKEND STORIES 22 e2e $httpBackend app/services/mock/MockServerModule.js angular.module('userRanking.mockServerModule', ['ngMockE2E', 'userRanking.urlsModule']) .run(['$httpBackend', 'urls', function($httpBackend, urls){ /*mocked data*/ $httpBackend.whenGET(urls.users) .respond([ { "id":"5", "name":"Alex", "image":"user5.png", "points": 210}, { "id":"6", "name":"Travis", "image":"user6.png", "points": 100} ]); }]) @enrique.oriol
  23. 23. $resource As a mechanism to load local JSON files
  24. 24. BACKEND STORIES 24 $resource https://docs.angularjs.org/api/ngResource/service/$resource 1. download Installation $ bower install angular-resource 2. include source <!-- mock server --> <script src="bower_components/angular-resource/angular-resource.js"></script> 3. include module angular.module('app', ['ngResource']); @enrique.oriol
  25. 25. BACKEND STORIES 25 $resource: return object with different methods, that populates async Usage $resource(url, [paramDefaults], [actions], options) Returned object methods $resource https://docs.angularjs.org/api/ngResource/service/$resource { 'get': {method:'GET'}, 'save': {method:'POST'}, 'query': {method:'GET', isArray:true}, 'remove': {method:'DELETE'}, 'delete': {method:'DELETE'} }; @enrique.oriol
  26. 26. BACKEND STORIES 26 $resource app/services/mock/MockServerModule.js angular.module('userRanking.mockServerModule', ['ngMockE2E','ngResource', 'userRanking.urlsModule']) .constant('fixturePaths', { "users": "fixtures/users.json" }) .run(['$httpBackend', 'urls', 'fixturePaths', '$resource', function($httpBackend, urls, fixturePaths, $resource){ /* allow local calls */ $httpBackend.whenGET(/fixtures/.*/).passThrough(); /*mocked data*/ $httpBackend.whenGET(urls.users).respond( $resource(fixturePaths.users).query() ); }]) @enrique.oriol
  27. 27. BACKEND STORIES 27 If you perform a $http call just after app boostrap, it’s possible that $resource is not already populated, returning an empty value. In that case you should perform some additional trick, always trying to perform all the stuff in MockServerModule, so it’s easier to “shut down” mock server when real server comes to life. You can see a decorator approach in the example code available on github. REMEMBER that $resource is populated ASYNC $resource @enrique.oriol
  28. 28. Dynamic responses The power behind mocking server with E2E $httpBackend
  29. 29. BACKEND STORIES 29 Dynamic responses Using function inside $httpBackend.respond <!-- mocked data --> $httpBackend.whenGET(urls.users).respond( $resource(fixturePaths.users).query() ); /*mocked data*/ var usersResource = $resource(fixturePaths.users).query(); $httpBackend.whenGET(urls.users).respond(function(method, url, data, headers){ return [200, usersResource, {}]; }); Changing this: Into this: Gives us access to url, data and headers, so our response can be dynamic @enrique.oriol
  30. 30. BACKEND STORIES 30 Dynamic responses We can perform data validation, and return custom headers .run(['$httpBackend', 'urls', 'fixturePaths', '$resource', function($httpBackend, urls, fixturePaths, $resource){ /*...previous stuff...*/ /* regex allowing any user id in user url*/ var anyUserUrl = new RegExp(urls.user('.*')); /*mock POST rx of a new user, and return auth token*/ $httpBackend.whenPOST(anyUserUrl).respond(function(method, url, data, headers){ data = angular.fromJson(data); if(data.name === undefined || !angular.isString(data.name) || data.name.length === 0){ return [400, null, null]; } else{ var user = { "id": "7", "name": data.name, "points": 0 }; return [200, user, {Authorization: "1234567890asdfghjklzxcvbnm"}]; } }); }]) @enrique.oriol
  31. 31. BACKEND STORIES 31 Dynamic responses We lack persistence .run(['$httpBackend', 'urls', 'fixturePaths', '$resource', function($httpBackend, urls, fixturePaths, $resource){ /*...previous stuff...*/ /*mock POST rx of a new user, and return auth token*/ $httpBackend.whenPUT(anyUserUrl).respond( function(method, url, data, headers){ data = angular.fromJson(data); if(headers['Authorization'] !== "Token 1234567890asdfghjklzxcvbnm"){ return [401, null, null]; } else if(data.name === undefined && data.points === undefined && data.image ===undefined){ return [400, null, null]; } else{ /*here we need to return updated data, so our app can reflect changes*/ var user = {...}; return [200, user, {}]; } }); }]) @enrique.oriol
  32. 32. Backend persistence Completing the experience with localStorage
  33. 33. BACKEND STORIES 33 Write in LS $window.localStorage['myKey'] = angular.toJson(myData); Read from LS var data = angular.fromJson($window.localStorage['myKey']); Usage Backend persistence Local Storage @enrique.oriol
  34. 34. BACKEND STORIES 34 .constant('lsMock', { "users": "ls-cached-users" }) .factory('mockedUsers', ['$window', '$resource', 'fixturePaths', 'lsMock', function($window, $resource, fixturePaths, lsMock){ var service = {}; service.list = angular.fromJson($window.localStorage[lsMock.users]); if(!service.list || service.list.length === 0){ service.list = $resource(fixturePaths.users).query(); } service.save = function(){ $window.localStorage[lsMock.users] = angular.toJson(service.list); }; service.add = function(user){ service.list.push(user); service.save(); }; Backend persistence Define a service to handle with cached users service.update = function(user){ for(var i=0; i<service.list.length; i++){ if(service.list[i].id == user.id){ service.list[i].points = user.points; service.save(); return; } } }; service.getById = function(userId){ for(var i=0; i<service.list.length; i++){ if(service.list[i].id == userId){ return service.list[i]; } } return null; }; return service; }]) @enrique.oriol
  35. 35. BACKEND STORIES 35 .run(['$httpBackend', '$resource', 'urls', 'mockedUsers', function($httpBackend, $resource, urls, mockedUsers){ /* local calls */ $httpBackend.whenGET(/fixtures/.*/).passThrough(); /* regex allowing any user id in user url*/ var anyUserUrl = new RegExp(urls.user('.*')); /*mocked users list*/ $httpBackend.whenGET(urls.users).respond(function(method, url, data, headers){ return [200, mockedUsers.list]; }); Backend persistence Using our service in mock server - whenGET @enrique.oriol
  36. 36. BACKEND STORIES 36 /* mock update user*/ $httpBackend.whenPUT(anyUserUrl).respond( function(method, url, data, headers){ data = angular.fromJson(data); var userId = url.replace(urls.user(''), ''); if(!data.id) {data.id = userId;} if(headers['Authorization'] !== "Token 1234567890asdfghjklzxcvbnm"){ return [401, null, null]; } else if(data.name === undefined && data.points === undefined && data.image ===undefined){ return [400, null, null]; } else{ mockedUsers.update(data); data = mockedUsers.getById(userId); return [200, data, {}]; } }); Backend persistence Using our service in mock server - - whenPUT @enrique.oriol
  37. 37. BACKEND STORIES 37 /* mock new user*/ $httpBackend.whenPOST(anyUserUrl).respond( function(method, url, data, headers){ data = angular.fromJson(data); if(data.name === undefined || !angular.isString(data.name) || data.name.length === 0){ return [400, null, null]; } else{ var user = { "id": ''+ mockedUsers.list.length, "name": data.name, "points": 0 }; mockedUsers.add(user); return [200, user, {Authorization: "1234567890asdfghjklzxcvbnm"}]; } }); }]) Backend persistence Using our service in mock server - - whenPOST @enrique.oriol
  38. 38. You’re ready Time to play with code http://kaikcreator.github.io/UserRanking/
  39. 39. THANK YOU! http://lnkdin.me/enriqueoriol blog.enriqueoriol.com @enrique.oriol

×