Share
Embed
Transcript
54 slides
-
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} -
Goals • Working knowledge • Client side JavaScript frameworks •
Know how to integrate them with existing services • Become more of a Cross Stack developer -
Book Information Service Book Jacket API Rating API Book Info
Website Browser -
Book Information Service DB Queues Logic Responsive Extendible Clean Parallel
Serve JS Frontend-y Book Jacket API Rating API Book Info Website Browser -
Book Information Service Silex PHP AngularJS JavaScript Express JavaScript Book
Jacket API Rating API Book Info Website Browser -
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} -
Book Information Service Book Jacket API Rating API Book Info
Website Browser -
//- 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) -
<!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> -
// 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: '/' }); }]); -
//- 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' ]); -
// 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) -
//- book-info-website/views/partials/list.jade div(ng-controller="IndexController") h1= title h2 Book list! ul li(ng-repeat="book
in books") a(href="#/{{book.isbn}}") {{book.title}} -
// 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(); }); } ] ); -
// book-info-website/public/javascripts/angular/controllers.js var booksControllers = angular.module('booksControllers', []); booksControllers.controller( 'IndexController',
['$scope', 'Book', function ($scope, Book) { $scope.books = Book.query(); }] ); … -
// 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; }]); -
// 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; }]); -
// 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(); }); } ] ); -
// 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); } }; -
// book-info-website/public/javascripts/angular/services.js api.stopPushGet = function () { pushing = false;
if (angular.isDefined(promise)) { $timeout.cancel(promise); promise = undefined; } }; -
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 -
Book Information Service Book Jacket API Rating API Book Info
Website Browser -
// 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; -
// 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); -
// 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; -
// book-info-website/routes/index.js /* GET home page. */ router.get('/', function(req, res,
next) { res.render('index'); }); … -
//- 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) -
// 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; -
// book-info-website/routes/book-api-v1.js router.get('/', function (req, res, next) { booksApiClient.findAll(function (err,
books) { res.json(books); }); }); -
// 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(); -
// 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); }); }; -
// 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; -
// 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); }); }); -
Alternatives? • Akka/Storm/React • Queues + Any language • Go/Rust/Other
language with parallelisation -
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