More Related Content

Similar to EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js(20)

More from Rob Tweed(20)

EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js

  1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 44 Creating MicroServices with QEWD.js Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  2. Copyright © 2016 M/Gateway Developments Ltd Using JSON Web Tokens with QEWD REST Services • This part of the course assumes that you've taken, at least: • Part 31: Using QEWD for Web and REST Services • http://www.slideshare.net/robtweed/ewd-3-training-course-part-31-ewdxpress-for-web-and-rest-services • Part 43: Using JSON Web Tokens with QEWD REST Services – https://www.slideshare.net/robtweed/ewd-3-training-course-part-43-using-json-web-tokens-with-qewd-rest-services
  3. Copyright © 2016 M/Gateway Developments Ltd MicroServices: Background • For an introduction on MicroServices, what they are, how they work and when and why they should be considered, see this overview on QEWD.js and MicroServices: • https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices
  4. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics Pharmacy ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Orchestration HTTPS WebSocket Connections
  5. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming REST request
  6. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request
  7. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request Process Locally?
  8. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request Handled by remote MicroService?
  9. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Persistent Bi-directional WebSocket connection Secured over HTTPS ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Remote QEWD MicroService
  10. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Architecture • Incoming requests can be either from: – a browser over WebSockets or Ajax – REST over HTTP / HTTPS • This tutorial will focus on REST
  11. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Architecture • MicroService routes are defined in the QEWD Startup file • On startup, WebSocket connections are established between the primary orchestrating QEWD system(s) and the MicroService QEWD Systems • MicroService Routing is handled by the QEWD Master Process
  12. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Architecture • Incoming requests to the orchestrating QEWD Server are over REST/HTTP(S) • If the requests are destined for a MicroService, they are converted to QEWD WebSocket messages • Incoming requests on MicroService QEWD systems are handled as QEWD WebSocket requests
  13. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Architecture • QEWD MicroServices rely on JWTs for authentication and Session/State management • All participating QEWD systems must be configured with the same JWT secret
  14. Copyright © 2016 M/Gateway Developments Ltd Let's Get Started!
  15. Copyright © 2016 M/Gateway Developments Ltd Simple Example • Two QEWD systems – One will be the primary orchestrating system that will be the endpoint for incoming REST requests • We'll refer to this as the Primary QEWD Server – One will be used as a user authentication service • We'll refer to this as the Login MicroService server
  16. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login MicroService Server
  17. Copyright © 2016 M/Gateway Developments Ltd Configuring the Primary Server
  18. Copyright © 2016 M/Gateway Developments Ltd Startup file from Part 43 var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes);
  19. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: {}, routes: [] } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes);
  20. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: {}, routes: [] } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); Two parts to the MicroService Configuration: -destinations: defines the endpoints -routes – assigns URL paths to destinations
  21. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [] } };
  22. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [] } }; You give each destination a name (your choice what it's called)
  23. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [] } }; A destination defines: -a QEWD host endpoint URL
  24. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [] } }; A destination defines: -The Application name assigned for this destination (ie the application on the remote machine that will handle incoming requests for this service destination)
  25. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } };
  26. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } Each Route is defined by an array element
  27. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } Each Route is defined by: - A URL path (which can be templated)
  28. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } Each Route is defined by: -optionally, an HTTP method If not specified, the route will apply to all HTTP methods for the path
  29. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } Each Route is defined by: -the MicroService destination to which the request will be routed
  30. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } Each Route is defined by: -the MicroService destination to which the request will be routed
  31. Copyright © 2016 M/Gateway Developments Ltd MicroService Routes Override Local Routes • Look in the Startup file and you'll see it also contains a route for locally processing all incoming REST requests starting /api: var routes = [ {path: '/api', module: 'myRestService'} ];
  32. Copyright © 2016 M/Gateway Developments Ltd MicroService Routes Override Local Routes • Look in the Startup file and you'll see it also contains a route for locally processing all incoming REST requests starting /api: • Our MicroService route for /api/login will take preference over this var routes = [ {path: '/api', module: 'myRestService'} ];
  33. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } You can define as many Routes and Destinations as you like
  34. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' }, info_service: { host: 'http://192.168.1.115:8080', application: 'info-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] Destinations can be at different physical endpoints
  35. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' }, info_service: { host: 'http://192.168.1.114:8080', application: 'info-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] The same physical endpoint can appear in more than one Destination
  36. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' }, info_service: { host: 'http://192.168.1.114:8080', application: 'info-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] The same physical endpoint can appear in more than one Destination In which case the application will be different
  37. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' }, info_service: { host: 'http://192.168.1.115:8080', application: 'info-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/info', method: 'GET', destination: 'info_service' } ] } Each Route can use different Destinations
  38. Copyright © 2016 M/Gateway Developments Ltd Add the MicroService Definition u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/userinfo', method: 'GET', destination: 'login_service' } ] } Each Route can use different Destinations or the same ones
  39. Copyright © 2016 M/Gateway Developments Ltd Our Example MicroService Definition jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } }; In our example we'll just define a single Destination and Route POST requests for /api/login will be routed to the MicroService QEWD system at 192.168.1.114:8080, where they will be handled by the login-micro-service application
  40. Copyright © 2016 M/Gateway Developments Ltd Our Example MicroService Definition var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.114:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } }; var routes = [ { path: '/api', module: 'myRestService', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes); Save as ~/qewd/ms-startup.js Change the IP address/ domain name of the login_service host property to match the server you'll be using
  41. Copyright © 2016 M/Gateway Developments Ltd Next we'll configure the MicroService System
  42. Copyright © 2016 M/Gateway Developments Ltd Next we'll configure the MicroService System • Remember – we'll now be working on your second QEWD server • As our starting point, we'll use that original startup file from Part 43 again…
  43. Copyright © 2016 M/Gateway Developments Ltd Startup file from Part 43 var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes);
  44. Copyright © 2016 M/Gateway Developments Ltd MicroService Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); Important: we'll use the same JWT Secret
  45. Copyright © 2016 M/Gateway Developments Ltd MicroService Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); We don't need this, however, even though this QEWD system will be handling forwarded requests for /api/login
  46. Copyright © 2016 M/Gateway Developments Ltd MicroService Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var qewd = require('qewd').master; qewd.start(config); That's because the primary QEWD system will re-format the /api/login REST requests to a WebSocket message
  47. Copyright © 2016 M/Gateway Developments Ltd MicroService Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var qewd = require('qewd').master; qewd.start(config); That's because the primary QEWD system will re-format the /api/login REST requests to a WebSocket message These messages will be handled by the login_micro-service application on this QEWD system
  48. Copyright © 2016 M/Gateway Developments Ltd MicroService Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' } }; var qewd = require('qewd').master; qewd.start(config); Save this file as: ~/qewd/loginservice.js
  49. Copyright © 2016 M/Gateway Developments Ltd Start Up the Two QEWD Systems • It doesn't matter which you start up first • Let's start the main one first and see what happens
  50. Copyright © 2016 M/Gateway Developments Ltd Start Up the Two QEWD Systems On the primary QEWD machine: cd ~/qewd node ms-startup
  51. Copyright © 2016 M/Gateway Developments Ltd Look at the QEWD Node Console Log rtweed@ubuntu:~/qewd$ node microservices Setting up micro-service connections Adding MicroService Client connection: url = http://192.168.1.114:8080; application = login-micro-service starting microService connection to http://192.168.1.114:8080 webServerRootPath = /home/rtweed/qewd/www/ route /api will be handled by qx.router
  52. Copyright © 2016 M/Gateway Developments Ltd Look at the QEWD Node Console Log rtweed@ubuntu:~/qewd$ node microservices Setting up micro-service connections Adding MicroService Client connection: url = http://192.168.1.114:8080; application = login-micro-service starting microService connection to http://192.168.1.114:8080 webServerRootPath = /home/rtweed/qewd/www/ route /api will be handled by qx.router QEWD hasn't fully started yet It's waiting for a response from the MicroService machine on 192.168.1.114:8080 Which, of course, hasn't been started yet
  53. Copyright © 2016 M/Gateway Developments Ltd Start Up the Two QEWD Systems On the QEWD Login MicroService machine: cd ~/qewd node loginservice
  54. Copyright © 2016 M/Gateway Developments Ltd Look at the QEWD Node Console Log on the MicroService machine rtweed@ubuntu:~/qewd$ node loginService webServerRootPath = /home/rtweed/qewd/www/ Worker Bootstrap Module file written to node_modules/ewd-qoper8-worker.js ======================================================== ewd-qoper8 is up and running. Max worker pool size: 2 ======================================================== QEWD.js is listening on port 8080 ======================================================== ** sockets: incoming message received: {"type":"ewd-register","application":"login-micro-service","jwt":true,"socketId":"L7OwOn Siqhr-nAmdAAAA","ipAddress":"::ffff:192.168.1.119"} no available workers sent qoper8-start message to 30140 process.argv[2] = qewd.worker workerModule: qewd; worker Session Garbage Collector has started in worker 30140 Tue, 22 Aug 2017 09:47:46 GMT; master process received response from worker 30140: {"type":"workerProcessStarted","ok":30140} new worker 30140 started and ready so process queue again Tue, 22 Aug 2017 09:47:46 GMT; worker 30140 received message: {"type":"ewd-register","application":"login-micro-service","jwt": true,"socketId":"L7OwOnSiqhr-nAmdAAAA","ipAddress":"::ffff:192.168.1.119"} Tue, 22 Aug 2017 09:47:46 GMT; master process received response from worker 30140: {"type":"ewd-register","finished":true, "message":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTU1NjYsImlhdCI6MTUwMzM5NTI2Niwia XNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjozMDAsInFld2QiOiJmZTc0 U5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.DsDntCU3x2w21K3vmK3iaJd1kOhIj_Rl9h1 0469qXu4"}} *** handleMessage response {"type":"ewd-register","finished":true,"message":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e hOTE0OGQ4Nzg1N2ZiZWM5Nzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM 0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMmE4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.D sDntCU3x2w21K3vmK3iaJd1kOhIj_Rl9h10469qXu4"}} Response time: 313ms
  55. Copyright © 2016 M/Gateway Developments Ltd Look at the QEWD Node Console Log on the MicroService machine rtweed@ubuntu:~/qewd$ node loginService webServerRootPath = /home/rtweed/qewd/www/ Worker Bootstrap Module file written to node_modules/ewd-qoper8-worker.js ======================================================== ewd-qoper8 is up and running. Max worker pool size: 2 ======================================================== QEWD.js is listening on port 8080 ======================================================== ** sockets: incoming message received: {"type":"ewd-register","application":"login-micro-service","jwt":true,"socketId":"L7OwOn Siqhr-nAmdAAAA","ipAddress":"::ffff:192.168.1.119"} no available workers sent qoper8-start message to 30140 process.argv[2] = qewd.worker workerModule: qewd; worker Session Garbage Collector has started in worker 30140 Tue, 22 Aug 2017 09:47:46 GMT; master process received response from worker 30140: {"type":"workerProcessStarted","ok":30140} new worker 30140 started and ready so process queue again Tue, 22 Aug 2017 09:47:46 GMT; worker 30140 received message: {"type":"ewd-register","application":"login-micro-service","jwt": true,"socketId":"L7OwOnSiqhr-nAmdAAAA","ipAddress":"::ffff:192.168.1.119"} Tue, 22 Aug 2017 09:47:46 GMT; master process received response from worker 30140: {"type":"ewd-register","finished":true, "message":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTU1NjYsImlhdCI6MTUwMzM5NTI2Niwia XNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjozMDAsInFld2QiOiJmZTc0 U5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.DsDntCU3x2w21K3vmK3iaJd1kOhIj_Rl9h1 0469qXu4"}} *** handleMessage response {"type":"ewd-register","finished":true,"message":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e hOTE0OGQ4Nzg1N2ZiZWM5Nzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM 0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMmE4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.D sDntCU3x2w21K3vmK3iaJd1kOhIj_Rl9h10469qXu4"}} Response time: 313ms QEWD has fully started
  56. Copyright © 2016 M/Gateway Developments Ltd Notice This Activity… Tue, 22 Aug 2017 09:47:46 GMT; worker 30140 received message: {"type":"ewd-register", "application":"login-micro-service","jwt":true,"socketId":"L7OwOnSiqhr-nAmdAAAA", "ipAddress":"::ffff:192.168.1.119"} Tue, 22 Aug 2017 09:47:46 GMT; master process received response from worker 30140: {"type":"ewd-register","finished":true,"message":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUz I1NiJ9.eyJleHAiOjE1MDMzOTU1NjYsImlhdCI6MTUwMzM5NTI2NiwiaXNzIjoicWV3ZC5qd3Q iLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjozMDAsInFld2Q iOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MWQ2M2FmYjQ3YTU5M2JkZDczYjNhOTE0OG Q4Nzg1N2ZiZWM5Nzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4ZjA1MTcxNGUxMT ViNGJhMjU2OGEzNjQyZGM0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMmE4MzU5ZTZhNDRjNG ZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.DsDntCU3x2w21K3vmK3iaJ d1kOhIj_Rl9h10469qXu4"}} *** handleMessage response {"type":"ewd-register","finished":true,"message":{"token":"eyJ0e XAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTU1NjYsImlhdCI6MTUwMzM5NTI 2NiwiaXNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ 0aW1lb3V0IjozMDAsInFld2QiOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MWQ2M2FmYjQ3Y TU5M2JkZDczYjNhOTE0OGQ4Nzg1N2ZiZWM5Nzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdj ZDUyMjE4ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM0NWNhZjljYjgwMjM2ZjM0M2Q0O WIwMmE4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3I n0.DsDntCU3x2w21K3vmK3iaJd1kOhIj_Rl9h10469qXu4"}} Response time: 313ms
  57. Copyright © 2016 M/Gateway Developments Ltd What Happened? • When the QEWD MicroService machine started, the primary machine was able to establish a WebSocket connection to it • It then registered the login-micro-service application on the QEWD MicroService machine, using a JWT
  58. Copyright © 2016 M/Gateway Developments Ltd Now look at the Primary QEWD Machine's Console Log rtweed@ubuntu:~/qewd$ node microservices Setting up micro-service connections Adding MicroService Client connection: url = http://192.168.1.114:8080; application = login-micro-service starting microService connection to http://192.168.1.114:8080 webServerRootPath = /home/rtweed/qewd/www/ route /api will be handled by qx.router login-micro-service registered http://192.168.1.114:8080 micro-service ready Worker Bootstrap Module file written to node_modules/ewd-qoper8-worker.js ======================================================== ewd-qoper8 is up and running. Max worker pool size: 2 ======================================================== QEWD.js is listening on port 8080 ======================================================== Connection was established to the Login MicroService QEWD system, and the login-micro-service application was registered
  59. Copyright © 2016 M/Gateway Developments Ltd Now look at the Primary QEWD Machine's Console Log rtweed@ubuntu:~/qewd$ node microservices Setting up micro-service connections Adding MicroService Client connection: url = http://192.168.1.114:8080; application = login-micro-service starting microService connection to http://192.168.1.114:8080 webServerRootPath = /home/rtweed/qewd/www/ route /api will be handled by qx.router login-micro-service registered http://192.168.1.114:8080 micro-service ready Worker Bootstrap Module file written to node_modules/ewd-qoper8-worker.js ======================================================== ewd-qoper8 is up and running. Max worker pool size: 2 ======================================================== QEWD.js is listening on port 8080 ======================================================== So QEWD has now fully started Both QEWD systems are ready!
  60. Copyright © 2016 M/Gateway Developments Ltd Startup Sequence Doesn't Matter • You can try stopping both QEWD systems (just CTRL & C each of them) • Restart the Login MicroService QEWD system first – It will start up fully and wait for incoming requests • Then start the primary QEWD system – It will connect to the Login MicroService QEWD machine immediately – You'll see the Login machine receive the registration request – The primary machine will then start QEWD
  61. Copyright © 2016 M/Gateway Developments Ltd You can shut down and restart either server • You can try stopping and restarting either QEWD system • After a brief pause, you'll see the two servers re- communicating and re-registering
  62. Copyright © 2016 M/Gateway Developments Ltd Ready to test the /api/login request • Use a REST Client • POST the /api/login request to the primary QEWD server
  63. Copyright © 2016 M/Gateway Developments Ltd Try POST /api/login
  64. Copyright © 2016 M/Gateway Developments Ltd Try POST /api/login Not surprising – we haven't yet written a handler For the login-micro-service application to handle This request on the MicroService machine…..but...
  65. Copyright © 2016 M/Gateway Developments Ltd Look at the Console Log for both machines • You'll see that they both burst into life
  66. Copyright © 2016 M/Gateway Developments Ltd Primary Machine sent: {"application":"login-micro-service","type":"restRequest","path":"/api/login", "pathTemplate":"/api/login","method":"POST","headers":{"host":"192.168.1.119:8080", "content-length":"41","content-type":"application/json"},"params":{"type":"login"},"query":{}, "body":{"username":"rob","password":"secret"},"ip":"::ffff:192.168.1.74","ips":[],"token":"eyJ 0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTk1NzMsImlhdCI6MTUwMz M5OTI3MywiaXNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcn ZpY2UiLCJ0aW1lb3V0IjozMDAsInFld2QiOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MW Q2MzhlZWM1OTZiMmJlYzU1YTI5MTRlODljNThmNTE4YTgwNzk2YTc4MmYwYmRmMm Y0MjFlZTZjNTdjZDUyMjE4ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM0NWNhZjljYj gwMjM2ZjM0M2Q0OWIwMmE4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZ jc4MzEyODhlZGJkOTE3In0.Re4a6cEmIzQ9PFhwwYsoQ-UtMBKHkTPSpAk55FCDyV8", "args":{},"jwt":true} received: {"type":"restRequest","finished":true,"message":{"error":"Unable to load handler module for: login-micro-service","reason":{"code":"MODULE_NOT_FOUND"}}, "responseTime":"14ms"}
  67. Copyright © 2016 M/Gateway Developments Ltd Login MicroService Machine Tue, 22 Aug 2017 10:54:33 GMT; worker 30188 received message: {"application": "login-micro-service","type":"restRequest","path":"/api/login","pathTemplate":"/api/login", "method":"POST","headers":{"host":"192.168.1.119:8080","content-length":"41", "content-type":"application/json"},"params":{"type":"login"},"query":{},"body":{"username":"rob" ,"password":"secret"},"ip":"::ffff:192.168.1.74","ips":[],"token":"eyJ0eXAiOiJKV1QiLCJhbGci OiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTk1NzMsImlhdCI6MTUwMzM5OTI3MywiaXNzIjoicWV 3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0Ijoz MDAsInFld2QiOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MWQ2MzhlZWM1OTZiMmJlYz U1YTI5MTRlODljNThmNTE4YTgwNzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4 ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMm E4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.R e4a6cEmIzQ9PFhwwYsoQ-UtMBKHkTPSpAk55FCDyV8","args":{},"jwt":true} Unable to load handler module for: login-micro-service: Error: Cannot find module 'login-micro-service' Tue, 22 Aug 2017 10:54:33 GMT; master process received response from worker 30188: {"type":"restRequest","finished":true,"message":{"error":"Unable to load handler module for: login-micro-service","reason":{"code":"MODULE_NOT_FOUND"}}} *** handleMessage response {"type":"restRequest","finished":true,"message":{ "error":"Unable to load handler module for: login-micro-service","reason":{ "code":"MODULE_NOT_FOUND"}}}
  68. Copyright © 2016 M/Gateway Developments Ltd Login MicroService Machine Tue, 22 Aug 2017 10:54:33 GMT; worker 30188 received message: {"application": "login-micro-service","type":"restRequest","path":"/api/login","pathTemplate":"/api/login", "method":"POST","headers":{"host":"192.168.1.119:8080","content-length":"41", "content-type":"application/json"},"params":{"type":"login"},"query":{},"body":{"username":"rob" ,"password":"secret"},"ip":"::ffff:192.168.1.74","ips":[],"token":"eyJ0eXAiOiJKV1QiLCJhbGci OiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTk1NzMsImlhdCI6MTUwMzM5OTI3MywiaXNzIjoicWV 3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0Ijoz MDAsInFld2QiOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MWQ2MzhlZWM1OTZiMmJlYz U1YTI5MTRlODljNThmNTE4YTgwNzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4 ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMm E4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.R e4a6cEmIzQ9PFhwwYsoQ-UtMBKHkTPSpAk55FCDyV8","args":{},"jwt":true}
  69. Copyright © 2016 M/Gateway Developments Ltd Login MicroService Machine Tue, 22 Aug 2017 10:54:33 GMT; worker 30188 received message: {"application": "login-micro-service","type":"restRequest","path":"/api/login","pathTemplate":"/api/login", "method":"POST","headers":{"host":"192.168.1.119:8080","content-length":"41", "content-type":"application/json"},"params":{"type":"login"},"query":{},"body":{"username":"rob" ,"password":"secret"},"ip":"::ffff:192.168.1.74","ips":[],"token":"eyJ0eXAiOiJKV1QiLCJhbGci OiJIUzI1NiJ9.eyJleHAiOjE1MDMzOTk1NzMsImlhdCI6MTUwMzM5OTI3MywiaXNzIjoicWV 3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0Ijoz MDAsInFld2QiOiJmZTc0NjBhM2ZjOGFmMWY5YjhlMWY1MWQ2MzhlZWM1OTZiMmJlYz U1YTI5MTRlODljNThmNTE4YTgwNzk2YTc4MmYwYmRmMmY0MjFlZTZjNTdjZDUyMjE4 ZjA1MTcxNGUxMTViNGJhMjU2OGEzNjQyZGM0NWNhZjljYjgwMjM2ZjM0M2Q0OWIwMm E4MzU5ZTZhNDRjNGZlNTYyZTM2NGE0ZjJjOTBmOGRjZjc4MzEyODhlZGJkOTE3In0.R e4a6cEmIzQ9PFhwwYsoQ-UtMBKHkTPSpAk55FCDyV8","args":{},"jwt":true}
  70. Copyright © 2016 M/Gateway Developments Ltd Incoming WebSocket Message { "application": "login-micro-service", "type": "restRequest", "path": "/api/login", "pathTemplate": "/api/login", "method": "POST", "headers": { "host": "192.168.1.119:8080", "content-length": "41", "content-type": "application/json" }, "params": { "type": "login" }, "query": {}, "body": { "username": "rob", "password": "secret" }, "ip": "::ffff:192.168.1.74", "ips": [], "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ey...", "args": {}, "jwt": true }
  71. Copyright © 2016 M/Gateway Developments Ltd Login MicroService Machine Unable to load handler module for: login-micro-service: Error: Cannot find module 'login-micro-service'
  72. Copyright © 2016 M/Gateway Developments Ltd Login MicroService Machine Tue, 22 Aug 2017 10:54:33 GMT; master process received response from worker 30188: {"type":"restRequest","finished":true,"message":{"error":"Unable to load handler module for: login-micro-service","reason":{"code":"MODULE_NOT_FOUND"}}} *** handleMessage response {"type":"restRequest","finished":true,"message":{ "error":"Unable to load handler module for: login-micro-service","reason":{ "code":"MODULE_NOT_FOUND"}}}
  73. Copyright © 2016 M/Gateway Developments Ltd So let's add a Login Handler • On the Login MicroService machine – 192.168.1.114 in our example – Create a new text file: • ~/qewd/node_modules/login-micro-service.js
  74. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module • We could write a standard QEWD WebSocket message handler function to deal with the re-packaged incoming message – Here's what it looked like
  75. Copyright © 2016 M/Gateway Developments Ltd Incoming WebSocket Message { "application": "login-micro-service", "type": "restRequest", "path": "/api/login", "pathTemplate": "/api/login", "method": "POST", "headers": { "host": "192.168.1.119:8080", "content-length": "41", "content-type": "application/json" }, "params": { "type": "login" }, "query": {}, "body": { "username": "rob", "password": "secret" }, "ip": "::ffff:192.168.1.74", "ips": [], "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ey...", "args": {}, "jwt": true }
  76. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module • We could write a standard QEWD WebSocket message handler function to deal with the re-packaged incoming message • But QEWD provides a set of shortcuts to make it even simpler – And to allow you to define multiple handler functions based on the original URL paths, using templated paths if you wish
  77. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } };
  78. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; First we need to define an init() function in which we'll define our URL routes
  79. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Inside this function, we define a routes object for all the routes we want to handle. We only have one: POST /api/login
  80. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Inside this function, we define a routes object for all the routes we want to handle. We only have one: POST /api/login And it will invoke a handler function named login() (which we haven't yet written)
  81. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Then we can run this QEWD API which constructs the handler function stubs for us within the module
  82. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; function login(args, finished) { // this will handle the login message } module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Next we define the login function
  83. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; function login(args, finished) { // this will handle the login message } module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Next we define the login() function It's the same function interface that you use for standard REST route handling The addMicroServiceHandler() API normalises the interface for you
  84. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; function login(args, finished) { // this will handle the login message } module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Next we define the login function It's the same function interface that you use for standard REST route handling args is an object containing: -req: incoming request object -session: the JWT payload
  85. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module var router = require('qewd-router'); var routes; function login(args, finished) { // this will handle the login message } module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; Next we define the login function It's the same function interface that you use for standard REST route handling -finished is the function for returning the response and releasing the worker process
  86. Copyright © 2016 M/Gateway Developments Ltd Incoming WebSocket Message { "application": "login-micro-service", "type": "restRequest", "path": "/api/login", "pathTemplate": "/api/login", "method": "POST", "headers": { "host": "192.168.1.119:8080", "content-length": "41", "content-type": "application/json" }, "params": { "type": "login" }, "query": {}, "body": { "username": "rob", "password": "secret" }, "ip": "::ffff:192.168.1.74", "ips": [], "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ey...", "args": {}, "jwt": true } We POSTed {"username":"rob","password":"secret"} So the user login credentials will be in the message body object
  87. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login( args, finished) { var username = args.req.body.username; var password = args.req.body.password; if (username === 'rob' && password === 'secret') { // valid login credentials } else { // invalid login – return an error } } We'll just hard-code the valid credentials for simplicity in this example
  88. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; // return success response } else { // invalid login – return an error } } Update the session object With our own data
  89. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; // return success response } else { // invalid login – return an error } } These are reserved session properties authenticated helps determine that the user was properly logged in timeout determines the JWT timeout each time QEWD updates it
  90. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); // return success response } else { // invalid login – return an error } } Make these two session properties secret. QEWD will encrypt them within the JWT so they aren't accesible or usable by the client. They will be available, however, for your back-end handler functions
  91. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); finished({ok: true}); } else { // invalid login – return an error } } Return a success response object and release the worker process
  92. Copyright © 2016 M/Gateway Developments Ltd Login Handler Module function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); finished({ok: true}); } else { return finished({error: 'Invalid login'}); } } Return an error response object and release the worker process
  93. Copyright © 2016 M/Gateway Developments Ltd Save the Login Handler Module var router = require('qewd-router'); var routes; function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); return finished({ok: true}); } else { return finished({error: 'Invalid login'}); } } module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } }; ~/qewd/node_modules/login-micro-service.js
  94. Copyright © 2016 M/Gateway Developments Ltd Restart the QEWD on the Login machine and re-try POST /api/login Now it works!
  95. Copyright © 2016 M/Gateway Developments Ltd Restart the QEWD on the Login machine and re-try POST /api/login Notice that although we just asked to return {ok: true}, it's returned an updated JWT also
  96. Copyright © 2016 M/Gateway Developments Ltd Let's decode that JWT
  97. Copyright © 2016 M/Gateway Developments Ltd Let's decode that JWT Here are the non-secret properties we set in the login() function
  98. Copyright © 2016 M/Gateway Developments Ltd Let's decode that JWT The secret ones are encrypted into this claim
  99. Copyright © 2016 M/Gateway Developments Ltd Try an invalid POST /api/login request Notice that although we just asked to return {ok: true}, it's returned an updated JWT also
  100. Copyright © 2016 M/Gateway Developments Ltd Try an invalid POST /api/login request There's the error message We created in the login() function
  101. Copyright © 2016 M/Gateway Developments Ltd Try an invalid POST /api/login request Note that when an error is reported, the JWT is not updated
  102. Copyright © 2016 M/Gateway Developments Ltd We now have a working MicroService!
  103. Copyright © 2016 M/Gateway Developments Ltd We now have a working MicroService • Let's now add a local API to the primary QEWD system – that can't be run until the user has logged in via the Login MicroService
  104. Copyright © 2016 M/Gateway Developments Ltd Already Have a Local Route Defined • Look in the Startup file and you'll see it also contains a route for locally processing all incoming REST requests starting /api: var routes = [ { path: '/api', module: 'myRestService', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ];
  105. Copyright © 2016 M/Gateway Developments Ltd Already Have a Local Route Defined • Look in the Startup file and you'll see it also contains a route for locally processing all incoming REST requests starting /api: var routes = [ { path: '/api', module: 'myRestService', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ]; All /api requests other than /api/login will be handled locally by the myRestService Worker process Handler module
  106. Copyright © 2016 M/Gateway Developments Ltd Already Have a Local Route Defined • Look in the Startup file and you'll see it also contains a route for locally processing all incoming REST requests starting /api: var routes = [ { path: '/api', module: 'myLocalServices', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ]; Let's change the name of the handler module to myLocalServices, to avoid confusion
  107. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module On the primary QEWD machine: ~/qewd/node_modules/myLocalServices.js var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { }, beforeHandler: function(req, finished) { } };
  108. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { routes = [ { url: '/api/info', method: 'GET', handler: info } ] routes = router.initialise(routes, module.exports); }, beforeHandler: function(req, finished) { } }; Define a route to be handled GET /api/info will be handled by a function named info()
  109. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { routes = [ { url: '/api/info', method: 'GET', handler: info } ] routes = router.initialise(routes, module.exports); }, beforeHandler: function(req, finished) { } }; Tell QEWD to set up the Handler function stubs Automatically within the module
  110. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { routes = [ { url: '/api/info', method: 'GET', handler: info } ] routes = router.initialise(routes, module.exports); }, beforeHandler: function(req, finished) { } }; The beforeHandler() function will be invoked for every incoming /api request We'll use this to confirm that the user has logged in before allowing the incoming request to be handled
  111. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module beforeHandler: function(req, finished) { // invoked for every incoming request, // before the route-specific handler } If the beforeHandler() function returns false, then the route-specific handler won't get called. Any other return value (including undefined) will allow the route-specific handler to run
  112. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module beforeHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Add this code inside the beforeHandler() function
  113. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module beforeHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Use this built-in API to extract, validate and decrypt the incoming JWT in the request's Authorization header All incoming requests will now need to have a Valid JWT, otherwise they'll be rejected
  114. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module beforeHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Returns an error, releases the worker process and ceases any further handling of the request if: -No Authorization Header was found -No JWT was found in the Authorization Header -The JWT signature wasn't valid -The JWT had expired
  115. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module beforeHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Returns an error, releases the worker process and ceases any further handling of the request if: -No Authorization Header was found -No JWT was found in the Authorization Header -The JWT signature wasn't valid -The JWT had expired Otherwise, adds the extracted, decrypted payload to the request as a Session object, and allows the route-specific handler to be invoked
  116. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module var router = require('qewd-router'); var routes; function info(args, finished) { } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/info', method: 'GET', handler: info } ] routes = router.initialise(routes, module.exports); }, beforeHandler: function(req, finished) { //... etc } }; Now we'll add the info() function
  117. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } Add this code inside the info() function
  118. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } By the time the info() handler function runs, the Session object has been made available as args.session. We'll add a new property to the Session: -ranInfoAt which is the time when the function was run
  119. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } username was specified as a private JWT/Session value by the Login MicroService. However, our handler function can access it because this server has the same JWT Secret
  120. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } You'll be able to see the decrypted, unpacked JWT payload displayed in the Node.js Console log
  121. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } We'll create a new JWT from the updated Session contents This will also update the JWT's expiry time
  122. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } We'll return some items of Information about the server We'll also display the username (which was a secret claim in The JWT)
  123. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } And finally we'll also return the updated JWT The Worker process will then be released
  124. Copyright © 2016 M/Gateway Developments Ltd Create a new Handler Module function info(args, finished) { args.session.ranInfoAt = Date.now(); var username = args.session.username; console.log('*** info args: ' + JSON.stringify(args, null, 2)); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ info: { server: 'ubuntu119', loggedInAs: username, arch: process.arch, platform: process.platform, versions: process.versions, memory: process.memoryUsage() }, token: jwt }); } Save as ~/qewd/node_modules/myLocalServices.js
  125. Copyright © 2016 M/Gateway Developments Ltd Restart the Primary QEWD System and Try the GET /api/info request
  126. Copyright © 2016 M/Gateway Developments Ltd Add an invalid Authorization Header
  127. Copyright © 2016 M/Gateway Developments Ltd Add an invalid JWT
  128. Copyright © 2016 M/Gateway Developments Ltd Now Login using the Login MicroService Note: Make sure you remove the Authorization Header!
  129. Copyright © 2016 M/Gateway Developments Ltd Successful Login
  130. Copyright © 2016 M/Gateway Developments Ltd Paste the JWT into the Authorization Header and try GET /api/info request again
  131. Copyright © 2016 M/Gateway Developments Ltd Success!
  132. Copyright © 2016 M/Gateway Developments Ltd Check the Node.js Console Logs • When you run the POST /api/login request, you'll see activity on both servers – Request passed to Login MicroService and response returned to primary QEWD server
  133. Copyright © 2016 M/Gateway Developments Ltd Check the Node.js Console Logs • When you run the GET /api/info request, there's only activity on the primary QEWD Server as it's handled locally – But it's only allowing the request to be handled locally if the JWT from the Login MicroService is present
  134. Copyright © 2016 M/Gateway Developments Ltd Adding More Local Routes • You can add as many additional local routes as you want to the primary QEWD server's Worker Process handler module • The beforeHandler() function will be automatically invoked before any of them are handled, so all will be authenticated • Each one should follow the pattern we used for the /api/info route definition and info() function
  135. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroServices • You should now have all the information you need to build a set of MicroServices using QEWD systems
  136. Copyright © 2016 M/Gateway Developments Ltd Advanced QEWD MicroServices • In the next Part of this course, we'll look at the advanced MicroServices features of QEWD, including: – Dynamic path-defined destinations – Federated composite MicroServices from a group of destinations – Re-direction of MicroService responses to another MicroService – Chained MicroServices