Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AngularJS and MicroServices

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

AngularJS and MicroServices

Avatar for Billie Thompson

Billie Thompson

July 02, 2015
Tweet

More Decks by Billie Thompson

Other Decks in Technology

Transcript

  1. Code/Demo Server book-jacket-api book-info-website Book Jacket API Rating API Book

    Info Website Browser ratings-api {server}.herokuapp.com https://github.com/PurpleBooth/{server}
  2. Goals • Working knowledge • Client side JavaScript frameworks •

    Know how to integrate them with existing services • Become more of a Cross Stack developer
  3. Book Information Service DB Queues Logic Responsive Extendible Clean Parallel

    Serve JS Frontend-y Book Jacket API Rating API Book Info Website Browser
  4. Code/Demo Server book-jacket-api book-info-website Book Jacket API Rating API Book

    Info Website Browser ratings-api {server}.herokuapp.com https://github.com/PurpleBooth/{server}
  5. //- book-info-website/views/index.jade doctype html
 html(lang="en" ng-app="booksApp")
 head
 title Book Info

    Website
 link(rel='stylesheet', href='/stylesheets/style.css')
 script(src='/assets/modules/angular/angular.js')
 script(src='/assets/modules/angular-resource/angular-resource.js')
 script(src='/assets/modules/angular-route/angular-route.js')
 script(src='/javascripts/angular/app.js')
 script(src='/javascripts/angular/services.js')
 script(src='/javascripts/angular/controllers.js')
 body
 div(ng-view)
  6. <!DOCTYPE html>
 <html lang="en" ng-app="booksApp">
 
 <head>
 <title>Book Info Website</title>


    <link rel="stylesheet" href="/stylesheets/style.css">
 <script src="/assets/modules/angular/angular.js"></script>
 <script src="/assets/modules/angular-resource/angular-resource.js"></script>
 <script src="/assets/modules/angular-route/angular-route.js"></script>
 <script src="/javascripts/angular/app.js"></script>
 <script src="/javascripts/angular/services.js"></script>
 <script src="/javascripts/angular/controllers.js"></script>
 </head>
 
 <body>
 <div ng-view></div>
 </body>
 
 </html>
  7. // book-info-website/public/javascripts/angular/app.js
 var booksApp = angular.module('booksApp', [
 'ngResource', 
 'ngRoute',


    'booksServices',
 'booksControllers'
 ]);
 
 booksApp.config(['$routeProvider',
 function($routeProvider) {
 $routeProvider.
 when('/', {
 templateUrl: 'partials/list.html',
 controller: 'IndexController'
 }).
 when('/:isbn', {
 templateUrl: 'partials/details.html',
 controller: 'DetailController'
 }).
 otherwise({
 redirectTo: '/'
 });
 }]);
  8. //- book-info-website/views/index.jade html(lang="en" ng-app=“booksApp”) … // book-info-website/public/javascripts/angular/app.js 
 var booksApp

    = angular.module('booksApp', [
 'ngResource', 
 'ngRoute',
 'booksServices',
 'booksControllers'
 ]); 

  9. // book-info-website/public/javascripts/angular/app.js booksApp.config(['$routeProvider',
 function($routeProvider) {
 $routeProvider.
 when('/', {
 templateUrl: 'partials/list.html',


    controller: 'IndexController'
 }).
 when('/:isbn', {
 templateUrl: 'partials/details.html',
 controller: 'DetailController'
 }).
 otherwise({
 redirectTo: '/'
 });
 }]); … //- book-info-website/views/index.jade body
 div(ng-view)
  10. // book-info-website/public/javascripts/angular/controllers.js
 var booksControllers = angular.module('booksControllers', []);
 
 booksControllers.controller('IndexController', ['$scope',

    'Book', function ($scope, Book) {
 Book.query({}, function (books) {
 $scope.books = books;
 });
 }]);
 
 booksControllers.controller(
 'DetailController',
 ['$scope', '$routeParams', 'Book',
 function ($scope, $routeParams, Book) {
 Book.pushGet({isbn: $routeParams.isbn}, function (book) {
 $scope.book = book;
 });
 
 $scope.$on('$destroy', function () {
 Book.stopPushGet();
 });
 }
 ]
 );
  11. // book-info-website/public/javascripts/angular/services.js
 var booksService = angular.module('booksServices', []);
 
 booksService.factory('Book', ['$resource',

    '$timeout',
 function ($resource, $timeout) {
 var api = $resource('api/v2/:isbn', {'isbn': '@id'});
 
 var promise;
 var pushing = false;
 
 api.pushGet = function (params, callback) {
 if (!pushing) {
 pushing = true;
 
 var refreshData = function () {
 api.get(params, function (book) {
 callback(book);
 
 promise = $timeout(
 function () {
 refreshData(params, callback);
 }, 4000);
 });
 };
 
 refreshData(params, callback);
 }
 };
 
 api.stopPushGet = function () {
 pushing = false;
 
 if (angular.isDefined(promise)) {
 $timeout.cancel(promise);
 promise = undefined;
 }
 };
 
 return api;
 }]);
  12. // book-info-website/public/javascripts/angular/services.js var booksService = angular.module('booksServices', []);
 
 booksService.factory('Book', ['$resource',

    '$timeout',
 function ($resource, $timeout) {
 var api = $resource('api/v2/:isbn', {'isbn': '@id'});
 …
 return api;
 }]);

  13. // book-info-website/public/javascripts/angular/controllers.js booksControllers.controller(
 'DetailController',
 ['$scope', '$routeParams', 'Book',
 function ($scope, $routeParams,

    Book) {
 Book.pushGet({isbn: $routeParams.isbn}, function (book) {
 $scope.book = book;
 });
 
 $scope.$on('$destroy', function () {
 Book.stopPushGet();
 });
 }
 ]
 );
  14. // book-info-website/public/javascripts/angular/services.js var promise;
 var pushing = false;
 
 api.pushGet

    = function (params, callback) {
 if (!pushing) {
 pushing = true;
 
 var refreshData = function () {
 api.get(params, function (book) {
 callback(book);
 
 promise = $timeout(
 function () {
 refreshData(params, callback);
 }, 4000);
 });
 };
 
 refreshData(params, callback);
 }
 };
  15. // book-info-website/public/javascripts/angular/services.js api.stopPushGet = function () {
 pushing = false;


    
 if (angular.isDefined(promise)) {
 $timeout.cancel(promise);
 promise = undefined;
 }
 };
  16. Future of frontend development • Simpler than it looks •

    Has a lot of similarities with server side frameworks • Fast, as data is loaded after the bare minimum
  17. // book-info-website/app.js
 var express = require('express');
 var path = require('path');


    var favicon = require('serve-favicon');
 var logger = require('morgan');
 var cookieParser = require('cookie-parser');
 var bodyParser = require('body-parser');
 
 var routes = require('./routes/index');
 var bookApi1 = require('./routes/book-api-v1');
 var bookApi2 = require('./routes/book-api-v2');
 
 var app = express();
 
 // view engine setup
 app.set('views', path.join(__dirname, 'views'));
 app.set('view engine', 'jade');
 
 // uncomment after placing your favicon in /public
 //app.use(favicon(__dirname + '/public/favicon.ico'));
 app.use(logger('dev'));
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: false }));
 app.use(cookieParser());
 app.use(express.static(path.join(__dirname, 'public')));
 app.use("/assets/modules", express.static(__dirname + '/bower_components'));
 
 app.use('/', routes);
 app.use('/api/v1', bookApi1);
 app.use('/api/v2', bookApi2);
 
 // catch 404 and forward to error handler
 app.use(function(req, res, next) {
 var err = new Error('Not Found');
 err.status = 404;
 next(err);
 });
 
 // error handlers
 
 // development error handler
 // will print stacktrace
 if (app.get('env') === 'development') {
 app.use(function(err, req, res, next) {
 res.status(err.status || 500);
 res.render('error', {
 message: err.message,
 error: err
 });
 });
 }
 
 // production error handler
 // no stacktraces leaked to user
 app.use(function(err, req, res, next) {
 res.status(err.status || 500);
 res.render('error', {
 message: err.message,
 error: {}
 });
 });
 
 
 module.exports = app;
  18. // book-info-website/app.js … 
 var routes = require('./routes/index');
 var bookApi1

    = require('./routes/book-api-v1');
 var bookApi2 = require('./routes/book-api-v2'); … app.use("/assets/modules", express.static(__dirname + '/bower_components'));
 
 app.use('/', routes);
 app.use('/api/v1', bookApi1);
 app.use('/api/v2', bookApi2); 

  19. // book-info-website/routes/index.js
 var express = require('express');
 var router = express.Router();


    
 /* GET home page. */
 router.get('/', function (req, res, next) {
 res.render('index');
 });
 
 /* GET home page. */
 router.get('/partials/details.html', function (req, res, next) {
 res.render('partials/details');
 });
 
 /* GET home page. */
 router.get('/partials/list.html', function (req, res, next) {
 res.render('partials/list');
 });
 
 
 module.exports = router;
  20. //- book-info-website/views/index.jade 
 doctype html
 html(lang="en" ng-app="booksApp")
 head
 title Book

    Info Website
 link(rel='stylesheet', href='/stylesheets/style.css')
 script(src='/assets/modules/angular/angular.js')
 script(src='/assets/modules/angular-resource/angular- resource.js')
 script(src='/assets/modules/angular-route/angular-route.js')
 script(src='/javascripts/angular/app.js')
 script(src='/javascripts/angular/services.js')
 script(src='/javascripts/angular/controllers.js')
 body
 div(ng-view)
  21. // book-info-website/routes/book-api-v1.js
 var express = require('express');
 
 var booksApiClient =

    require('./../api-client/books');
 
 var router = express.Router();
 
 router.get('/', function (req, res, next) {
 booksApiClient.findAll(function (err, books) {
 res.json(books);
 });
 });
 
 router.get('/:isbn', function (req, res, next) {
 booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {
 res.json(bookDetail);
 });
 });
 
 module.exports = router;
  22. // book-info-website/api-client/books.js
 var makeHttpRequest = require('./client');
 
 var Books =

    function () {
 
 var endpoint = "http://book-jacket-api.herokuapp.com/";
 
 this.getBook = function (isbn, callback) {
 makeHttpRequest(endpoint.concat(isbn), callback)
 };
 
 this.findAll = function (callback) {
 makeHttpRequest(endpoint, callback)
 };
 };
 
 module.exports = new Books();
  23. // book-info-website/api-client/client.js var http = require('http');
 
 module.exports = function

    (url, callback) {
 http.get(url, function (res) {
 var str = '';
 
 res.on('data', function (chunk) {
 str += chunk;
 });
 
 res.on('end', function () {
 var apiResponse = JSON.parse(str);
 
 callback(null, apiResponse);
 });
 }).on('error', function (e) {
 callback(e);
 });
 };
  24. // book-info-website/routes/book-api-v2.js
 
 var express = require('express');
 var async =

    require('async');
 var _ = require('underscore');
 
 var booksApiClient = require('./../api-client/books');
 var ratingsApiClient = require('./../api-client/ratings');
 
 var router = express.Router();
 
 router.get('/', function (req, res, next) {
 booksApiClient.findAll(function (err, books) {
 res.json(books);
 });
 });
 
 router.get('/:isbn', function (req, res, next) {
 async.parallel({
 'book': function (callback) {
 booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {
 callback(err, bookDetail);
 });
 },
 'rating': function (callback) {
 ratingsApiClient.getRating(req.params.isbn, function (err, ratings) {
 callback(err, ratings);
 });
 }
 }, function (err, results) {
 _.extend(results.book, {'rating': results.rating});
 res.json(results.book);
 });
 });
 
 module.exports = router;
  25. // book-info-website/routes/book-api-v2.js router.get('/:isbn', function (req, res, next) {
 async.parallel({
 'book':

    function (callback) {
 booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {
 callback(err, bookDetail);
 });
 }, 
 'rating': function (callback) {
 ratingsApiClient.getRating(req.params.isbn, function (err, ratings) {
 callback(err, ratings);
 });
 } 
 }, function (err, results) {
 _.extend(results.book, {'rating': results.rating});
 res.json(results.book);
 });
 });

  26. In conclusion • Lower the barrier to entry to allow

    flexibility in you team • MVVM frameworks are going to get more popular • MVVM frameworks require APIs • Use middleware to hide none public services