SlideShare a Scribd company logo
1 of 254
Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 45
Using QEWD's Advanced
MicroService Functionality
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
Using QEWD's Advanced
MicroService Functionality
• 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
• Part 44: Creating MicroServices with QEWD.js
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
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
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
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
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?
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?
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
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
Copyright © 2016 M/Gateway Developments Ltd
Advanced QEWD
MicroServices
• In this Part of this course, we'll look at the
advanced MicroServices features of QEWD,
including:
– Templated routes
– Dynamic path-defined destinations
– Federated composite MicroServices from a group of
destinations
– Re-direction of MicroService responses to another
MicroService
– Chained MicroServices
Copyright © 2016 M/Gateway Developments Ltd
Example in Part 44
• In the previous part of this course we set
up an example with:
– A primary server
• One local service: /api/info
– A Login MicroService server
• One MicroService: /api/login
• /api/info could not be run until /api/login
had been run, and its returned JWT used
in the Authorization Header
Copyright © 2016 M/Gateway Developments Ltd
Example in Part 44
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
Copyright © 2016 M/Gateway Developments Ltd
Advanced MicroService Functionality
• To demonstrate the advanced MicroService
Functionality, we'll extend the examples used in Part 44
• Primary Server:
– Startup file: ~/qewd/ms-startup.js
– Local REST services:
• ~/qewd/node_modules/myLocalServices
• Login MicroService Server:
– Startup file: ~/qewd/loginservice.js
– MicroService Handler Module:
• ~/qewd/node_modules/login-micro-service.js
Copyright © 2016 M/Gateway Developments Ltd
Advanced MicroService Functionality
• To demonstrate the advanced MicroService
Functionality, we'll extend the examples used in Part 44
• In the example code, I'll assume the host addresses for
the servers are:
– Primary Server: 192.168.1.120:8080
– Login MicroService Server: 192.168.1.121:8080
• Adjust these to match your servers
Copyright © 2016 M/Gateway Developments Ltd
Templated Routes
• In part 44, the login MicroService route
was defined in the Primary Server's
startup file using an absolute route:
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
}
]
}
};
Copyright © 2016 M/Gateway Developments Ltd
Templated Routes
• Just as with QEWD's REST routing, your
MicroService Routes can define variable
components, eg:
routes: [
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login-micro-service'
}
]
Copyright © 2016 M/Gateway Developments Ltd
Templated Routes
• Templated Routes can contain any
number of variable parts, eg:
routes: [
{
path: '/api/patient/:patientId/headings/:headingName/info',
method: 'GET',
destination: 'login-micro-service'
}
]
Copyright © 2016 M/Gateway Developments Ltd
How Templated Routes Work
• The message sent to the MicroService will
contain:
– The actual path that was used
– The path template that it matched
– The values of the variable path components,
as name/value pairs
Copyright © 2016 M/Gateway Developments Ltd
How Templated Routes Work
• For example, if the specified route is
routes: [
{
path: '/api/patient/:patientId/headings/:headingName/info',
method: 'GET',
destination: 'patientInfo_service'
}
]
Copyright © 2016 M/Gateway Developments Ltd
How Templated Routes Work
• For example, if the specified route is
• and the actual request was:
– /api/patient/123456/headings/allergies/info
routes: [
{
path: '/api/patient/:patientId/headings/:headingName/info',
method: 'GET',
destination: 'patientInfo_service'
}
]
Copyright © 2016 M/Gateway Developments Ltd
How Templated Routes Work
• For example, if the specified route is
• and the actual request was:
– /api/patient/123456/headings/allergies/info
• Message to MicroService will include:
– Path: /api/patient/123456/headings/allergies/info
– Matched Template Path: /api/patient/:patientId/headings/:headingName/info
– Parameters: {patientId: 123456, headingName: 'allergies'}
routes: [
{
path: '/api/patient/:patientId/headings/:headingName/info',
method: 'GET',
destination: 'patientInfo_service'
}
]
Copyright © 2016 M/Gateway Developments Ltd
Example primary Server Startup File (1)
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
Note that both routes
have the same
destination
Copyright © 2016 M/Gateway Developments Ltd
Example primary Server Startup File (2)
var routes = [
{
path: '/api',
module: 'myLocalServices',
errors: {
notfound: {
text: 'Resource Not Recognised',
statusCode: 404
}
}
}
];
var qewd = require('qewd').master;
var q = qewd.start(config, routes);
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
• Define the templated route in your Worker
Process Handler module, eg
– ~/qewd/node_modules/login-micro-service.js
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
• Define the templated route in your Worker
Process Handler module, eg
– ~/qewd/node_modules/login-micro-service.js
init: function() {
routes = {
'/api/login': {
POST: login
}
};
router.addMicroServiceHandler(routes, module.exports);
}
This is what we'd created in Part 44 when we
defined the login MicroService
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
init: function() {
routes = {
'/api/login': {
POST: login
},
' /api/patient/:patientId/demographics ': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
}
Add the new templated route definition
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
init: function() {
routes = {
'/api/login': {
POST: login
},
' /api/patient/:patientId/demographics ': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
}
Specify the route template exactly the same as
on the primary server
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
init: function() {
routes = {
'/api/login': {
POST: login
},
' /api/patient/:patientId/demographics ': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
}
So how will the handler function – getDemographics()
in our example here – handle the variable path?
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
function getDemographics(args, finished) {
var patientId = args.patientId;
// ..etc
}
The values of variable path components are
automatically made available to you as properties
of the args object
It's identical to handling variable routes in standard
QEWD REST services (see Part 31)
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
var patients = {
'123456': {
firstName: 'Rob',
lastName: 'Tweed',
gender: 'Male',
country: 'UK'
},
'123457': {
firstName: 'Jane',
lastName: 'Smith',
gender: 'Female',
country: 'USA'
},
};
function getDemographics(args, finished) {
var patientId = args.patientId.toString();
if (!patientId || patientId === '') {
return finished({error: 'You must specify a patientId'});
}
if (!patients[patientId]) {
return finished({error: 'Invalid patientId'});
}
finished(patients[patientId]);
}
For example:
hard-coded simulation of
a patient database
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (1)
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'});
}
}
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (2)
var patients = {
'123456': {
firstName: 'Rob',
lastName: 'Tweed',
gender: 'Male',
country: 'UK'
},
'123457': {
firstName: 'Jane',
lastName: 'Smith',
gender: 'Female',
country: 'USA'
},
};
function getDemographics(args, finished) {
var patientId = args.patientId.toString();
if (!patientId || patientId === '') {
return finished({error: 'You must specify a patientId'});
}
if (!patients[patientId]) {
return finished({error: 'Invalid patientId'});
}
finished(patients[patientId]);
}
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (3)
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
},
'/api/patient/:patientId/demographics': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
}
};
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Stop and restart the startup files on the
Primary and MicroService servers
• In your REST Client:
– GET /api/patient/1234567/demographics
Copyright © 2016 M/Gateway Developments Ltd
Try it
Copyright © 2016 M/Gateway Developments Ltd
Try it
It successfully returned the results for patient 123457
Try changing the patientId in the path to 123456
Copyright © 2016 M/Gateway Developments Ltd
Security Issue
• We have a problem – the demographics
MicroService can be invoked by anyone
• It should only be accessible if you've
logged in using the Login MicroService
• We can fix this by adding a
beforeMicroServiceHandler() function into
the MicroService module
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (3)
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
},
'/api/patient/:patientId/demographics': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
},
beforeMicroServiceHandler: function(req, finished) {
// authenticate against the JWT (except for Login requests)
if (req.path !== '/api/login') {
return this.jwt.handlers.validateRestRequest.call(this, req, finished);
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (3)
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
},
'/api/patient/:patientId/demographics': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
},
beforeMicroServiceHandler: function(req, finished) {
// authenticate against the JWT (except for Login requests)
if (req.path !== '/api/login') {
return this.jwt.handlers.validateRestRequest.call(this, req, finished);
}
}
};
Ignore Login requests – no JWT needed for them
Copyright © 2016 M/Gateway Developments Ltd
Example MicroService Module (3)
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
},
'/api/patient/:patientId/demographics': {
GET: getDemographics
}
};
router.addMicroServiceHandler(routes, module.exports);
},
beforeMicroServiceHandler: function(req, finished) {
// authenticate against the JWT (except for Login requests)
if (req.path !== '/api/login') {
return this.jwt.handlers.validateRestRequest.call(this, req, finished);
}
}
};
This is the same API that we used in the previous
Part of the course. There we applied it to the
beforeHandler() function of the Local REST Service Module
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Stop and restart the startup files on the
Primary and MicroService servers
• In your REST Client:
– GET /api/patient/1234567/demographics
• Now you should get an error:
• Authorization Header missing or JWT not found in
header (expected format: Bearer {{JWT}}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• OK so try logging in:
• In your REST Client:
– POST /api/login
• {"username": "rob", "password": "secret"}
• If it logged you in successfully, copy the
returned JWT into the Authorization
Header:
• Authorization: Bearer {{JWT}}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Now try the Demographics request again
• In your REST Client:
– GET /api/patient/1234567/demographics
• Now it should work
• Only logged in users can invoke the
demographics request
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
init: function() {
routes = {
'/api/login': {
POST: login
},
' /api/patient/:patientId/headings/:heading/summary ': {
GET: getHeadingSummary
}
};
router.addMicroServiceHandler(routes, module.exports);
}
A path can include as many variable components
as you require. Here, we have :patientId and
:heading
Copyright © 2016 M/Gateway Developments Ltd
Handling Templated Routes on
MicroService Server
function getHeadingSummary(args, finished) {
var patientId = args.patientId;
var heading = args.heading;
// ..etc
}
The values of variable path components are
automatically made available to you as properties
of the args object
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• So far, our examples have used fixed
destinations:
– A URL route is tied to a specific destination,
eg: u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
}
]
}
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• You can also have dynamic routes
• :destination is a reserved URL path
component name
• It's mapped at run-time to the destination
actually specified in the URL, and the
request is routed to that physical
destination
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• For example, suppose we have several
stores, and we want to retrieve a list of
items in stock at a particular store
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• Define the store destinations in the
primary server's startup configuration:
u_services: {
destinations: {
store1: {
host: 'http://store1.retailer.com:8080',
application: 'stock-list'
},
store2: {
host: 'http://store2.retailer.com:8080',
application: 'stock-list'
},
store3: {
host: 'http://store3.retailer.com:8080',
application: 'stock-list'
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• Then define the route with the variable
destination:
u_services: {
destinations: {
//
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET'
}
]
}
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• Each store would have the stock-list
application module installed:
var router = require('qewd-router');
var routes;
function getStockList(args, finished) {
// collate the stock list for the store
var stockListObj = {
stock: 'stock list here...'
};
finished(stockListObj);}
module.exports = {
init: function() {
routes = {
'/api/store/:destination/stocklist': {
GET: getStockList
}
};
router.addMicroServiceHandler(routes, module.exports);
}
};
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• Each store would have the stock-list
application module installed:
var router = require('qewd-router');
var routes;
function getStockList(args, finished) {
// collate the stock list for the store
var stockListObj = {
stock: 'stock list here...'
};
finished(stockListObj);}
module.exports = {
init: function() {
routes = {
'/api/store/:destination/stocklist': {
GET: getStockList
}
};
router.addMicroServiceHandler(routes, module.exports);
}
};
URL path must be
specified identically
to primary server's
route
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• The client then specifies the URL:
– GET /api/store/store2/stocklist
– In this case, the request will be routed to the
destination named store2
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Routes
• The URL route can include other variable
components, eg
• The handler function getStockListByCategory()
would access the category using args.category
routes = {
'/api/store/:destination/category/:category/stocklist': {
GET: getStockListByCategory
},
}
Copyright © 2016 M/Gateway Developments Ltd
Adding Security
• Our earlier example included a Login
MicroService on a server specifically for
user authentication
• We've now added 3 stores as additional
locations
• We'll want to make sure that the stores
can't be interrogated until the user has
logged in
Copyright © 2016 M/Gateway Developments Ltd
Adding Security
• Just add the beforeMicroServiceHandler()
function to the handler module in each of
the stores:
beforeMicroServiceHandler: function(req, finished) {
return this.jwt.handlers.validateRestRequest.call(this, req, finished);
}
Every incoming request to each store will now need a valid
JWT that originated from a valid Login
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
Expre
ss
Nod
e.js
socke
t.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
Store 1
Store 2
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
ewd-qoper8
queue
Express
Node.j
s
socket.i
o
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
Store 3
Copyright © 2016 M/Gateway Developments Ltd
Grouped Destinations
Copyright © 2016 M/Gateway Developments Ltd
Grouped Destinations
• In the previous example, you requested
the stock list from a single store that you
specified
• What if you wanted to get a combined
stock listing from all stores?
• That's where Grouped Destinations can
be used
Copyright © 2016 M/Gateway Developments Ltd
Setting up a Group Destination
• On the primary server:
– Define the destination and any routes using
the group destination
• On the servers within the group
destination:
– Define the route(s) within the application
module
Copyright © 2016 M/Gateway Developments Ltd
Primary Server
• Within the startup file configuration:
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
},
store1: {
host: 'http://192.168.1.114:8080',
application: 'stock-list'
},
store2: {
host: 'http://192.168.1.119:8080',
application: 'stock-list'
},
allStores: {
destinations: ['store1', 'store2']
}
},
routes: [
]
}
Copyright © 2016 M/Gateway Developments Ltd
Primary Server
• Within the startup file configuration:
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
},
store1: {
host: 'http://192.168.1.114:8080',
application: 'stock-list'
},
store2: {
host: 'http://192.168.1.119:8080',
application: 'stock-list'
},
allStores: {
destinations: ['store1', 'store2']
}
},
routes: [
]
}
We're defining a new destination
named allStores which is a group
consisting of store1 and store2,
each of which must be separately
defined
Copyright © 2016 M/Gateway Developments Ltd
Primary Server
• Within the startup file configuration:
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
},
store1: {
host: 'http://192.168.1.114:8080',
application: 'stock-list'
},
store2: {
host: 'http://192.168.1.119:8080',
application: 'stock-list'
},
allStores: {
destinations: ['store1', 'store2']
}
},
routes: [
]
}
We're defining a new destination
named allStores which is a group
consisting of store1 and store2,
each of which must be separately
defined
Copyright © 2016 M/Gateway Developments Ltd
Primary Server
• Now define any routes for this group:
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET'
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
Copyright © 2016 M/Gateway Developments Ltd
On each Server in the Group
• Define route(s) in the application module
init: function() {
routes = {
'/api/store/:destination/stocklist': {
GET: getStockList
},
'/api/store/:destination/category/:category/stocklist': {
GET: getStockListByCategory
},
'/api/all/stocklist': {
GET: getStockList
}
};
router.addMicroServiceHandler(routes, module.exports);
},
Copyright © 2016 M/Gateway Developments Ltd
On each Server in the Group
• Define route(s) in the application module
init: function() {
routes = {
'/api/store/:destination/stocklist': {
GET: getStockList
},
'/api/store/:destination/category/:category/stocklist': {
GET: getStockListByCategory
},
'/api/all/stocklist': {
GET: getStockList
}
};
router.addMicroServiceHandler(routes, module.exports);
},
In this example, we'll just re-use the getStockList()
function that we previously defined
Copyright © 2016 M/Gateway Developments Ltd
Security / Authentication
• We've already added the
beforeMicroServiceHandler() function, so
you'll have to first login before you can use
the new Grouped destination request.
beforeMicroServiceHandler: function(req, finished) {
return this.jwt.handlers.validateRestRequest.call(this, req, finished);
}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Stop and restart the startup files on the
Primary and MicroService servers
• In your REST Client:
– GET /api/all/stocklist
• You should see activity on all machines,
but you should get an error:
• Authorization Header missing or JWT not found in
header (expected format: Bearer {{JWT}}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• OK so try logging in:
• In your REST Client:
– POST /api/login
• {"username": "rob", "password": "secret"}
• If it logged you in successfully, copy the
returned JWT into the Authorization
Header:
• Authorization: Bearer {{JWT}}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Now try again:
– GET /api/all/stocklist
• This time you should get a composite
result back from your store MicroServices
– Remember that we asked to run the same
getStockList function as before on each store
MicroService
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
Each store's results appears
within a results object,
identified by each destination's
name
Copyright © 2016 M/Gateway Developments Ltd
Try It
Response format for all
Grouped destinations
{results:
destination1: {results object},
destination2: {results object},
…etc
}
Copyright © 2016 M/Gateway Developments Ltd
Try It
A new JWT with updated
expiry is also returned
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Grouped Destination?
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
},
store1: {
host: 'http://192.168.1.114:8080',
application: 'stock-list'
},
store2: {
host: 'http://192.168.1.119:8080',
application: 'stock-list'
},
allStores: {
destinations: ['store1', 'store2']
}
},
routes: [
]
}
Our grouped destination – allStores – is
still just a destination.
Copyright © 2016 M/Gateway Developments Ltd
Dynamic Grouped Destination?
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET'
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
So what happens if
we use it for this route?
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
Almost identical results as
before!
A composite result from each
destination within the group
Is returned
Copyright © 2016 M/Gateway Developments Ltd
Try It
If you look carefully, there is
a small difference. This time
each store returns a store
property with a value of
allStores.
Copyright © 2016 M/Gateway Developments Ltd
Try It
That's because the getStockList()
function that ran on each store
returned the value of
args.destination
function getStockList(args, finished) {
// collate the stock list for the store
var stockListObj = {
store: args.destination,
ip: '192.168.1.119',
stock: 'stock list here...'
};
finished(stockListObj);
}
Copyright © 2016 M/Gateway Developments Ltd
Try It
That's because the getStockList()
function that ran on each store
returned the value of
args.destination
:destination was defined in the
dynamic route that we just used,
but not in the /api/all/stocklist route
we used previously
function getStockList(args, finished) {
// collate the stock list for the store
var stockListObj = {
store: args.destination,
ip: '192.168.1.119',
stock: 'stock list here...'
};
finished(stockListObj);
}
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• So far we've let QEWD automatically
return the responses to the REST client
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• So far we've let QEWD automatically
return the responses to the REST client
• What if you want to do something with the
response before it's returned?
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• So far we've let QEWD automatically
return the responses to the REST client
• What if you want to do something with the
response before it's returned, eg:
– Re-packaging / re-formatting of the response
– Conditional behaviour depending on the
response content
– Triggering a new request to another
MicroService
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• Each route that you define in your Primary
server's startup file can have an optional
additional property:
– onResponse
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• Each route that you define in your Primary
server's startup file can have an optional
additional property:
– onResponse
• You define a function to handle the response
• The function has a single argument, args, that
contains all you need to handle the response
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET'
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
Here's our routes as
defined in the Primary
Server's startup file
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET'
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
We'll add custom handling
to this one
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
console.log('** onResponse: ' + JSON.stringify(args));
}
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
Let's initially just inspect
that args object
Copyright © 2016 M/Gateway Developments Ltd
Try it
• First log in:
• In your REST Client:
– POST /api/login
• {"username": "rob", "password": "secret"}
• If it logged you in successfully, copy the
returned JWT into the Authorization
Header:
• Authorization: Bearer {{JWT}}
Copyright © 2016 M/Gateway Developments Ltd
Try it
• Now send this request:
– GET /api/store/store1/stocklist
• Now look in the Node.js console log for
your Primary server
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
received: {"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list
here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX
NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz
hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2
YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ
0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3
MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul-
VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"}
** onResponse: {"message":{"type":"ewd-qoper8-express","path":"/api/store/store1/stocklist","method":"GET","headers":
{"host":"192.168.1.120:8080","authorization":"Bearer
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NDQsImlhdCI6MTUwNDA4MzI0NCwiaXNzIjoicWV3ZC5
qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzh
hZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2Y
zU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ0
YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3M
TVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.8pY7HEBIdhWZEXR0WBeIJBGEgn-WpRwcwDZf-0uJ0AM","content-
type":"application/json"},"params":{"0":"store1/stocklist","type":"store"},"query":{},"body":{},"ip":"::ffff:192.168.1.74","ips":
[],"application":"api","expressType":"store"},"destination":"store1","responseObj":
{"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list
here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX
NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz
hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2
YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ
0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3
MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul-
VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"}}
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object{
"message": {
"type": "ewd-qoper8-express",
"path": "/api/store/store1/stocklist",
"method": "GET",
"headers": {
"host": "192.168.1.120:8080",
"authorization": "Bearer eyJ0eXAiOiJKV1..",
"content-type": "application/json"
},
"params": {
"0": "store1/stocklist",
"type": "store"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "store"
},
"destination": "store1",
"responseObj": {
"type": "restRequest",
"finished": true,
"message": {
"store": "store1",
"ip": "192.168.1.114",
"stock": "stock list here...",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..."
},
"responseTime": "8ms"
}
}
Let's prettify the args
Object, so we can
see what it contains
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
{
"message": {
"type": "ewd-qoper8-express",
"path": "/api/store/store1/stocklist",
"method": "GET",
"headers": {
"host": "192.168.1.120:8080",
"authorization": "Bearer eyJ0eXAiOiJKV1..",
"content-type": "application/json"
},
"params": {
"0": "store1/stocklist",
"type": "store"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "store"
},
args.message:
-The original incoming
request object
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
{
"destination": "store1",
}
args.destination:
-Any variable path components
will be available as properties
in their own right.
So the value of :destination in
our request is available to us
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
{
"responseObj": {
"type": "restRequest",
"finished": true,
"message": {
"store": "store1",
"ip": "192.168.1.114",
"stock": "stock list here...",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..."
},
"responseTime": "8ms"
}
}
args.responseObj:
-The raw response object
that was returned from
the MicroService
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
• It also contains two other properties that
aren't shown, as they are functions:
– args.handleResponse
– args.send
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
• It also contains two other properties that
aren't shown, as they are functions:
– args.handleResponse
• If you want to re-package / re-format the response,
you must use this function to return your response
to the REST client.
– args.send
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
• It also contains two other properties that
aren't shown, as they are functions:
– args.handleResponse
– args.send
• If you want to send a request to another
MicroService, you use this function
Copyright © 2016 M/Gateway Developments Ltd
onResponse args Object
• It also contains two other properties that
aren't shown, as they are functions:
– args.handleResponse
– args.send
• Note: if you use either of these functions,
your onResponse() function MUST return
true. This tells QEWD not to try to handle
the response itself
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
console.log('** onResponse: ' + JSON.stringify(args));
}
},
{
path: '/api/store/:destination/category/:category/stocklist',
method: 'GET'
},
{
path: '/api/all/stocklist',
method: 'GET',
destination: 'allStores'
}
]
}
Let's return our own
custom response…
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
If an error has occurred,
we'll let QEWD handle it
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
Otherwise, we'll
create our own
response object
from information in
the args object
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
Use the handleResponse()
function to return the
response to the REST
client
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
And finally tell QEWD
that we've handled the
response ourselves, so
it doesn't need to
Copyright © 2016 M/Gateway Developments Ltd
Try It
• Remember to first log in
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
There's our re-packaged
custom response
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
One thing to remember:
Your onResponse() function
is invoked in the QEWD
Master process
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
So make sure you don't
do anything CPU-intensive
or long-running
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
You should consider running
your logic in a QEWD worker
process.
If you do, you'd also have
access to the database
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var respObj = {
youSent: args.message.path,
usingMethod: args.message.method,
toStore: args.destination,
stock: args.responseObj.message.stock
};
args.handleResponse(respObj);
return true;
}
}
}
]
}
But how do you use a
QEWD Worker Process
from here?
It's actually very easy…
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true
};
}
}
}
]
}
First we create a message object
It must contain at least these 3
properties.
The value of type is up to you, but
use the values shown for token
and jwt
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true,
params: {
path: args.message.path,
method: args.message.method,
store: args.destination,
stock: args.responseObj.message.stock
}
};
}
}
}
]
}
Now we'll add the data we
want to send to the Worker
in our message
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true,
params: {
path: args.message.path,
method: args.message.method,
store: args.destination,
stock: args.responseObj.message.stock
}
};
this.handleMessage(msg, function(responseObj) {
args.handleResponse(responseObj);
});
}
}
}
]
}
this.handleMessage() is the ewd-qoper8
API for sending a message to a Worker.
Its callback allows us to process the
response from the Worker – we'll send
the response to the REST client, using
args.handleResponse()
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true,
params: {
path: args.message.path,
method: args.message.method,
store: args.destination,
stock: args.responseObj.message.stock
}
};
this.handleMessage(msg, function(responseObj) {
args.handleResponse(responseObj);
});
return true;
}
}
}
]
}
Finally, tell QEWD that we're
Handling the response
Copyright © 2016 M/Gateway Developments Ltd
Try It
• Login and put the JWT into the
Authorization header as before
• Then send:
– GET /api/store/store1/stocklist
• You should get back an error:
{"error": "Unable to load handler module for: stock-list"}
Copyright © 2016 M/Gateway Developments Ltd
Why That Error?
• It was expected that we'd get an error
back from the QEWD Worker process
– We haven't yet written a handler function for
the message type we sent to it
Copyright © 2016 M/Gateway Developments Ltd
Why That Error?
• It was expected that we'd get an error
back from the QEWD Worker process
– We haven't yet written a handler function for
the message type we sent to it
• But the error was because it couldn't find a
module named stock-list
– Why was it looking for a module of this name?
Copyright © 2016 M/Gateway Developments Ltd
Why That Error?
• It was expected that we'd get an error
back from the QEWD Worker process
– We haven't yet written a handler function for
the message type we sent to it
• But the error was because it couldn't find a
module named stock-list
– Why was it looking for a module of this name?
• It's because we sent the JWT as the
message token
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Example
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true,
params: {
path: args.message.path,
method: args.message.method,
store: args.destination,
stock: args.responseObj.message.stock
}
};
this.handleMessage(msg, function(responseObj) {
args.handleResponse(responseObj);
});
return true;
}
}
}
]
}
We sent the JWT that was returned
by the MicroService
Copyright © 2016 M/Gateway Developments Ltd
Time to Inspect the JWT
u_services: {
destinations: {
},
routes: [
{
path: '/api/store/:destination/stocklist',
method: 'GET',
onResponse: function(args) {
if (!args.responseObj.message.error) {
console.log('** token: ' + args.responseObj.message.token);
var msg = {
type: 'save',
token: args.responseObj.message.token,
jwt: true,
params: {
path: args.message.path,
method: args.message.method,
store: args.destination,
stock: args.responseObj.message.stock
}
};
this.handleMessage(msg, function(responseObj) {
args.handleResponse(responseObj);
});
return true;
}
}
}
]
}
We'll display the token in the
Node.js console log
Copyright © 2016 M/Gateway Developments Ltd
Time to Inspect the JWT
• Restart QEWD on the Primary server and
try sending that same request
• Then take a look in the Node.js console
log
Copyright © 2016 M/Gateway Developments Ltd
Time to Inspect the JWT
** token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwOTQwNjcsImlhd
CI6MTUwNDA5Mjg2NywiaXNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0
b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzhhZjF
mOWI4ZTFmNTFkNjNkN2FlNmM3ZjAzZjg0MmJjYjQ0OWNkZmU4Mjc4YTY5O
Dc5NmE3ODAwMGJkZjJmNDIxZWU2YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1Yj
RiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2
YTQ0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZ
DM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3MTVkIiwidXNlclRleHQiOiJXZ
Wxjb21lIFJvYiJ9.2xGoGnrdFjMf6rn5GEkOAlHamFPceQC6jiS4M6siPb0
Wed, 30 Aug 2017 11:34:27 GMT; worker 13037 received message:
{"type":"save","params":
{"path":"/api/store/store1/stocklist","method":"GET","store":"store1","stock":"stock
list
Copyright © 2016 M/Gateway Developments Ltd
Inspect the JWT using jwt.io
Copyright © 2016 M/Gateway Developments Ltd
Inspect the JWT using jwt.io
This is why the QEWD Worker
Is looking for a module named
stock-list
Copyright © 2016 M/Gateway Developments Ltd
Let's create that module
• On your Primary server, create a new text
file:
– ~/qewd/node_modules/stock-list.js
Copyright © 2016 M/Gateway Developments Ltd
Let's create that module
• On your Primary server, create a new text
file:
– ~/qewd/node_modules/stock-list.js
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
finished(messageObj);
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
Let's create that module
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
finished(messageObj);
}
}
};
For the purpose of this example/demo, we're
just going to send the incoming message
back to the Master process
Copyright © 2016 M/Gateway Developments Ltd
Let's create that module
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
finished(messageObj);
}
}
};
But so that we can be sure that a Worker really
handled it, we'll add this to the message
Copyright © 2016 M/Gateway Developments Ltd
Try It
• Restart QEWD on the Primary Server
• Login again and copy the JWT to the
Authorization Header
• Then send:
– GET /api/store/store1/stocklist
Copyright © 2016 M/Gateway Developments Ltd
Try It
You should see a response similar to this
Copyright © 2016 M/Gateway Developments Ltd
Try It
You can see it's the message we sent to the Worker for processing
Copyright © 2016 M/Gateway Developments Ltd
Try It
Here's the additional property added by our
Worker module's handler function
Copyright © 2016 M/Gateway Developments Ltd
By running in a Worker
• You have access to the integrated QEWD
database
• You have access to the Session data
contained within the JWT
Copyright © 2016 M/Gateway Developments Ltd
Let's enhance that module
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
var db = this.db.use('stockList');
var ix = db.increment();
db.$(ix).setDocument(messageObj);
finished(messageObj);
}
}
};
For example, we could save the message object
to the database
Copyright © 2016 M/Gateway Developments Ltd
Let's enhance that module
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
var db = this.db.use('stockList');
var ix = db.increment();
db.$(ix).setDocument(messageObj);
session.customHandledAt = Date.now();
finished(messageObj);
}
}
};
And we could add a new data item to the Session / JWT
Copyright © 2016 M/Gateway Developments Ltd
Try It
• Restart QEWD
• Login and copy the JWT to the
Authorization Header as before
• Send:
– GET /api/store/store1/stocklist
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Now Check the Database using
qewd-monitor
Click the
Document Store
Tab
Copyright © 2016 M/Gateway Developments Ltd
Now Check the Database using
qewd-monitor
Here's the saved
Document we
created, containing
the message we
sent to the Worker
Copyright © 2016 M/Gateway Developments Ltd
Reminder of how that happened
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
var db = this.db.use('stockList');
var ix = db.increment();
db.$(ix).setDocument(messageObj);
session.customHandledAt = Date.now();
finished(messageObj);
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
Now Inspect the JWT
Copy this token and paste
it into the jwt.io inspector
Copyright © 2016 M/Gateway Developments Ltd
Now Inspect the JWT
Here's the field we
added to the Session
and therefore to the JWT
Copyright © 2016 M/Gateway Developments Ltd
Reminder of how that happened
module.exports = {
handlers: {
save: function(messageObj, session, send, finished) {
messageObj.handledByWorker = process.pid + ' at ' + Date.now();
var db = this.db.use('stockList');
var ix = db.increment();
db.$(ix).setDocument(messageObj);
session.customHandledAt = Date.now();
finished(messageObj);
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• So far you've learned how to:
– re-package / re-format the response from a
MicroService
– Process the response from a MicroService, including:
• saving some or all of it to the QEWD database
• Modifying the JWT before returning the response to the
REST client
• You should be able to adapt the examples to
make the behaviour conditional on the response
content
Copyright © 2016 M/Gateway Developments Ltd
Custom Response Handling
• What if you wanted to trigger a new request to
another MicroService, eg:
– Make the /api/login request to the Primary Server:
• Invoke the call to the authentication MicroService to log us in
(using the username and password credentials), as we've
done already
• But then, if successful, make a call to a demographics
MicroService to get some basic information about the
logged-in user
• Add those demographic details to the response that is
returned to the REST client
Copyright © 2016 M/Gateway Developments Ltd
What we'll need to use:
• The args object, passed as an argument
to your onResponse() handler function
contains two properties are functions:
– args.handleResponse
– args.send
• If you want to send a request to another
MicroService, you use this function
Copyright © 2016 M/Gateway Developments Ltd
What we'll need to use:
• The args object, passed as an argument
to your onResponse() handler function
contains two properties are functions:
– args.handleResponse
• We've already seen how to use this to return our
customised response back to the REST client
– args.send
Copyright © 2016 M/Gateway Developments Ltd
What we'll need to use:
• The args object, passed as an argument
to your onResponse() handler function
contains two properties are functions:
– args.handleResponse
– args.send
• Now we'll use this function, which allows us to
send a new request to a MicroService
Copyright © 2016 M/Gateway Developments Ltd
We'll use these two routes
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
This is the startup
file for the Primary
server
Copyright © 2016 M/Gateway Developments Ltd
We'll use these two routes
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
The demographics
route was described
in detail at the start of
this Part of the course
Copyright © 2016 M/Gateway Developments Ltd
We'll use these two routes
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
For this example,
both routes are
handled by the same
destination, but they
could be at different
destinations
Copyright © 2016 M/Gateway Developments Ltd
We'll use these two routes
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
The demographics
route expects a
variable value – the
patientId
Let's first change
the login MicroService
so it returns an id
that we can use for
the demographics
route
Copyright © 2016 M/Gateway Developments Ltd
Modify the Login MicroService
• On your Login MicroService machine, find
the handler module:
– ~/qewd/node_modules/login-micro-service.js
– We'll edit the login() function
Copyright © 2016 M/Gateway Developments Ltd
Modify the Login MicroService
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.userId = 123456;
session.authenticated = true;
session.timeout = 1200;
session.makeSecret('username');
session.makeSecret('authenticated');
return finished({ok: true});
}
else {
return finished({error: 'Invalid login'});
}
}
Add this line
For simplicity, in this
Example, we're just
hard-coding an Id
In the real-world we'd
probably do some kind
of database lookup
Copyright © 2016 M/Gateway Developments Ltd
Modify the Login MicroService
• What this modification will do is add the
userId to the JWT that is returned to the
Primary server
• Stop and restart the Login MicroService
and try logging in again
Copyright © 2016 M/Gateway Developments Ltd
Modified Login MicroService Response
So the Login MicroService returns 2 properties: ok and token
Let's copy and paste the token and inspect it using jwt.io
Copyright © 2016 M/Gateway Developments Ltd
Modified Login MicroService Response
So here's the new userId field that we added
Copyright © 2016 M/Gateway Developments Ltd
Custom Login Response Handler
• We now need to go back to the startup file
on your Primary Server, and add an
onResponse() handler function to the
Login route definition
Copyright © 2016 M/Gateway Developments Ltd
Primary Server Startup File
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.121:8080',
application: 'login-micro-service',
onResponse: function(args) {
}
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service'
},
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'login_service'
}
]
}
};
We'll zoom in
here on the next
slides..
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
We'll only make a call to the demographics service
if the login was successful
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
Use this.jwt.handlers.getProperty() to extract a
public claim from the JWT – in this case the userId
This doesn't require the secret, so is lightweight
enough to perform in the master process
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
The first argument specifies the
Property or Claim name we want
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
The 2nd
argument is a JWT. We'll use
the JWT returned in the Login
MicroService response
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
Next, we construct a minimal message object to
send a demographics MicroService request
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
The path will match that of the demographics route
We add into the path the userId extracted from the
Login JWT
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
The HTTP method we want to use is GET
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
Finally, we must add the authorization header.
The format must be the same as if we'd
submitted the request from a REST client
We'll use the JWT returned by the Login MicroService
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
If the MicroService we want to use had a POST
method, we'd need to specify a body object in
the message.
If the URL needed additional name/value pairs,
we'd add a query object to the method
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
The message object should be sufficient for
the demographics MicroService, so now
we send it using the args.send() function
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
We want to combine the response from the
Login MicroService with what comes back from
the Demographics MicroService, so we'll do that
in the arg,send callback function
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
We'll get the ok property from the Login response
In other situations you might have many more
properties to merge into the final response
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
And now we're ready to return the composite
response back to the REST client.
We use args.handleResponse() for this, as before
Copyright © 2016 M/Gateway Developments Ltd
Modified Login Response Handler
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
onResponse: function(args) {
if (!args.responseObj.message.error) {
var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + args.responseObj.message.token
}
};
args.send(message, function(responseObj) {
responseObj.message.ok = args.responseObj.message.ok;
args.handleResponse(responseObj);
});
return true;
}
}
},
One last very important step – we must tell
QEWD that we're handling the response
Copyright © 2016 M/Gateway Developments Ltd
Try It
• Restart QEWD on the Primary and Login
MicroService servers
• Send:
– GET /api/login
• With the appropriate body payload containing the
username and password
Copyright © 2016 M/Gateway Developments Ltd
Try It
Copyright © 2016 M/Gateway Developments Ltd
Try It
Success!
Copyright © 2016 M/Gateway Developments Ltd
Try It
These properties came from the Demographics service
Copyright © 2016 M/Gateway Developments Ltd
Try It
And this came from the Login service
Copyright © 2016 M/Gateway Developments Ltd
Recap so far
• You've now learned how to:
– Use templated routes with variable path
components
– Add custom response handling
• And optionally perform some or all of that handling
in a QEWD worker process, allowing:
– use of the integrated QEWD document database
– The JWT contents to be manipulated
• And optionally send one or more further requests
to MicroServices and construct a composite
response
Copyright © 2016 M/Gateway Developments Ltd
All on the Primary Server
• Everything we've done so far in this Part of
the Course has been on the Primary
Server
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
& demographics
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 Request
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
& demographics
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 Request
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
& demographics
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 Response
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
& demographics
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
Demographics
Request
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
& demographics
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
WebSocket
Connections
Demographics
Response
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
& demographics
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
Composite
Response
Copyright © 2016 M/Gateway Developments Ltd
What about the MicroService Servers?
• Is it possible for a MicroService to make
requests to other MicroServices?
– In other words…
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
User authentication
Demographics
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 Request
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
User authentication
Demographics
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 Request
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
User authentication
Demographics
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
Demographics
Request
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
User authentication
Demographics
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
Demographics
Response
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
User authentication
Demographics
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 Response
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
User authentication
Demographics
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 Response
Copyright © 2016 M/Gateway Developments Ltd
Lets build out the 3 servers
• Primary Server
– Will handle the /api/login request sent from the REST
client
• Login Server
– Will handle the /api/login request sent from the
Primary Server
• Demographics Server
– Will handle the /api/patient/:patientId/demographics
request sent from the Login Server
Copyright © 2016 M/Gateway Developments Ltd
Primary Server
• Startup File
– ~/qewd/primary.js
Copyright © 2016 M/Gateway Developments Ltd
Primary Server (1)
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
}
]
}
};
Copyright © 2016 M/Gateway Developments Ltd
Primary Server (2)
var routes = [
{
path: '/api',
module: 'localServices'
}
];
var qewd = require('qewd').master;
var q = qewd.start(config, routes);
Copyright © 2016 M/Gateway Developments Ltd
Primary Server (1) - Notes
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.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
}
]
}
};
The Primary Server only
knows the route to the
Login Server
Copyright © 2016 M/Gateway Developments Ltd
Primary Server (1) - Notes
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'Primary QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
jwt: {
secret: 'someSecret123'
},
u_services: {
destinations: {
login_service: {
host: 'http://192.168.1.121:8080',
application: 'login-micro-service'
}
},
routes: [
{
path: '/api/login',
method: 'POST',
destination: 'login_service',
}
]
}
};
Adjust to match your
Login Server location
Copyright © 2016 M/Gateway Developments Ltd
Primary Server (2) - Notes
var routes = [
{
path: '/api',
module: 'localServices'
}
];
var qewd = require('qewd').master;
var q = qewd.start(config, routes);
Our example is only going
to respond to incoming
requests for /api/login which
it will forward to the Login
MicroService server.
We won't be handling any
other routes locally on this
server. Nevertheless,
the Primary Server needs
to know to recognise any
paths starting /api, so we
need to specify it as a
notional route
We'll specify a non-existent
local handler module named
localServices that won't
actually get used
Copyright © 2016 M/Gateway Developments Ltd
Login Server
• Startup file:
– ~/qewd/loginserver.js
• Worker Module for handling the /api/login
login requests
• ~/qewd/node_modules/login-micro-service.js
Copyright © 2016 M/Gateway Developments Ltd
Login Server: Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'QEWD Login MicroService',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
jwt: {
secret: 'someSecret123'
},
u_services: {
destinations: {
demographics: {
host: 'http://192.168.1.114:8080',
application: 'demographics'
}
},
routes: [
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'demographics'
}
]
}
};
var qewd = require('qewd').master;
qewd.start(config);
Copyright © 2016 M/Gateway Developments Ltd
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'QEWD Login MicroService',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
jwt: {
secret: 'someSecret123'
},
u_services: {
destinations: {
demographics: {
host: 'http://192.168.1.114:8080',
application: 'demographics'
}
},
routes: [
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'demographics'
}
]
}
};
var qewd = require('qewd').master;
qewd.start(config);
The Login Server needs
to know the route to
the Demographics
MicroService
Login Server: Startup File
Copyright © 2016 M/Gateway Developments Ltd
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'QEWD Login MicroService',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
jwt: {
secret: 'someSecret123'
},
u_services: {
destinations: {
demographics: {
host: 'http://192.168.1.114:8080',
application: 'demographics'
}
},
routes: [
{
path: '/api/patient/:patientId/demographics',
method: 'GET',
destination: 'demographics'
}
]
}
};
var qewd = require('qewd').master;
qewd.start(config);
Adjust to match your
Demographics
Server location
Login Server: Startup File
Copyright © 2016 M/Gateway Developments Ltd
Login Server: Worker Module (1)
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.userId = 123456;
session.authenticated = true;
session.timeout = 1200;
session.makeSecret('username');
session.makeSecret('authenticated');
return finished({ok: true});
}
else {
return finished({error: 'Invalid login'});
}
}
This file is too
long to fit on
one slide
This is part 1
See next slide
for the rest of
the file
Copyright © 2016 M/Gateway Developments Ltd
Login Server: Worker Module (2)
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
}
};
router.addMicroServiceHandler(routes, module.exports);
},
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
module.exports = {
init: function() {
routes = {
'/api/login': {
POST: login
}
};
router.addMicroServiceHandler(routes, module.exports);
},
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
Any QEWD Worker Module can have a workerResponseHandlers object
defined. It is used to define handler functions that are invoked in the Master
Process for any specified message type, once that message's worker-side
handler has finished
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
The workerResponseHandlers handler function for a message type is
invoked after the main handler function has completed, and before
the response is returned to the client. We can use this to intercept the
Normal MicroServices flow.
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
QEWD MicroServices always use a pre-determined message type of
restRequest. So we're defining a workerResponseHander function that
will intercept all microService responses.
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
So the first thing we must do is filter out the one(s) we're interested in. In fact
In the example there will only be one MicroService request type hitting this
Code – for /api/login - but you'll often need to know how to do this
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
For MicroService messages (ie of type restRequest), QEWD adds the path
(actually the path template) to the message argument.
So we can filter on this as shown
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
We need to know the user Id for the logged in user. So, just as we did
earlier, we'll extract it from the JWT. It's in message.token
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
Copyright © 2016 M/Gateway Developments Ltd
The key new piece
Now we can call out to the demographics MicroService. We create a
message object containing, at minimum, the path, method and headers
This is just the same as we did before when we called the MicroService
from the Primary Server
workerResponseHandlers: {
restRequest: function(message, send) {
if (message.path === '/api/login') {
var ok = message.ok;
var userId = this.jwt.handlers.getProperty('userId', message.token);
var message = {
path: '/api/patient/' + userId + '/demographics',
method: 'GET',
headers: {
authorization: 'Bearer ' + message.token
}
};
this.microServiceRouter.call(this, message, function(responseObj) {
responseObj.message.ok = ok;
send(responseObj);
});
return true;
}
}
}
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details
getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details

More Related Content

What's hot

ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Servicesewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST ServicesRob Tweed
 
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...Rob Tweed
 
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...Rob Tweed
 
EWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeEWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeRob Tweed
 
QEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooQEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooRob Tweed
 
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesEWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesRob Tweed
 
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationRob Tweed
 
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging CycleEWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging CycleRob Tweed
 
EWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionEWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionRob Tweed
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationRob Tweed
 
EWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 OverviewEWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 OverviewRob Tweed
 
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternEWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternRob Tweed
 
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationEWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationRob Tweed
 
EWD 3 Training Course Part 16: QEWD Services
EWD 3 Training Course Part 16: QEWD ServicesEWD 3 Training Course Part 16: QEWD Services
EWD 3 Training Course Part 16: QEWD ServicesRob Tweed
 
EWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsRob Tweed
 
EWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingEWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingRob Tweed
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDRob Tweed
 
EWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingRob Tweed
 
EWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceEWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceRob Tweed
 
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDEWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDRob Tweed
 

What's hot (20)

ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Servicesewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
 
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
 
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
 
EWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeEWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient Mode
 
QEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooQEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It Too
 
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesEWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
 
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
 
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging CycleEWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
 
EWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionEWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD Session
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
 
EWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 OverviewEWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 Overview
 
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternEWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
 
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationEWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
 
EWD 3 Training Course Part 16: QEWD Services
EWD 3 Training Course Part 16: QEWD ServicesEWD 3 Training Course Part 16: QEWD Services
EWD 3 Training Course Part 16: QEWD Services
 
EWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIs
 
EWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingEWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session Locking
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWD
 
EWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven Indexing
 
EWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceEWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a Service
 
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDEWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
 

Similar to getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details

REST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxREST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxJason452803
 
EWD 3 Training Course Part 6: What Happens when a QEWD Application is Started
EWD 3 Training Course Part 6: What Happens when a QEWD Application is StartedEWD 3 Training Course Part 6: What Happens when a QEWD Application is Started
EWD 3 Training Course Part 6: What Happens when a QEWD Application is StartedRob Tweed
 
Spring Cloud: API gateway upgrade & configuration in the cloud
Spring Cloud: API gateway upgrade & configuration in the cloudSpring Cloud: API gateway upgrade & configuration in the cloud
Spring Cloud: API gateway upgrade & configuration in the cloudOrkhan Gasimov
 
Java springboot microservice - Accenture Technology Meetup
Java springboot microservice - Accenture Technology MeetupJava springboot microservice - Accenture Technology Meetup
Java springboot microservice - Accenture Technology MeetupAccenture Hungary
 
Solid and Infrastructure as Code
Solid and Infrastructure as CodeSolid and Infrastructure as Code
Solid and Infrastructure as CodeBram Vogelaar
 
Microservices with Node.js and Apache Cassandra
Microservices with Node.js and Apache CassandraMicroservices with Node.js and Apache Cassandra
Microservices with Node.js and Apache CassandraJorge Bay Gondra
 
Open shift deployment review getting ready for day 2 operations
Open shift deployment review   getting ready for day 2 operationsOpen shift deployment review   getting ready for day 2 operations
Open shift deployment review getting ready for day 2 operationsHendrik van Run
 
OGCE Project Overview
OGCE Project OverviewOGCE Project Overview
OGCE Project Overviewmarpierc
 
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014Rackspace Academy
 
Intuit_payment_system_Craft_Demo_slide.pdf
Intuit_payment_system_Craft_Demo_slide.pdfIntuit_payment_system_Craft_Demo_slide.pdf
Intuit_payment_system_Craft_Demo_slide.pdfHaeyoon Jo
 
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023Simplify and Scale Enterprise Apps in the Cloud | Boston 2023
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023VMware Tanzu
 
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023VMware Tanzu
 
Enabling .NET Apps with Monitoring and Management Using Steeltoe
Enabling .NET Apps with Monitoring and Management Using SteeltoeEnabling .NET Apps with Monitoring and Management Using Steeltoe
Enabling .NET Apps with Monitoring and Management Using SteeltoeVMware Tanzu
 
Odata V4 : The New way to REST for Your Applications
Odata V4 : The New way to REST for Your Applications Odata V4 : The New way to REST for Your Applications
Odata V4 : The New way to REST for Your Applications Alok Chhabria
 
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...Felix Gessert
 
How to Contribute to Apache Usergrid
How to Contribute to Apache UsergridHow to Contribute to Apache Usergrid
How to Contribute to Apache UsergridDavid M. Johnson
 
Deep Dive: Strategic Importance of BaaS
Deep Dive: Strategic Importance of BaaSDeep Dive: Strategic Importance of BaaS
Deep Dive: Strategic Importance of BaaSApigee | Google Cloud
 
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...Puppet
 
Consuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL WebservicesConsuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL WebservicesEdwin Rojas
 

Similar to getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details (20)

REST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptxREST API 20.2 - Appworks Gateway Integration.pptx
REST API 20.2 - Appworks Gateway Integration.pptx
 
EWD 3 Training Course Part 6: What Happens when a QEWD Application is Started
EWD 3 Training Course Part 6: What Happens when a QEWD Application is StartedEWD 3 Training Course Part 6: What Happens when a QEWD Application is Started
EWD 3 Training Course Part 6: What Happens when a QEWD Application is Started
 
Spring Cloud: API gateway upgrade & configuration in the cloud
Spring Cloud: API gateway upgrade & configuration in the cloudSpring Cloud: API gateway upgrade & configuration in the cloud
Spring Cloud: API gateway upgrade & configuration in the cloud
 
Java springboot microservice - Accenture Technology Meetup
Java springboot microservice - Accenture Technology MeetupJava springboot microservice - Accenture Technology Meetup
Java springboot microservice - Accenture Technology Meetup
 
Solid and Infrastructure as Code
Solid and Infrastructure as CodeSolid and Infrastructure as Code
Solid and Infrastructure as Code
 
Microservices with Node.js and Apache Cassandra
Microservices with Node.js and Apache CassandraMicroservices with Node.js and Apache Cassandra
Microservices with Node.js and Apache Cassandra
 
Open shift deployment review getting ready for day 2 operations
Open shift deployment review   getting ready for day 2 operationsOpen shift deployment review   getting ready for day 2 operations
Open shift deployment review getting ready for day 2 operations
 
OGCE Project Overview
OGCE Project OverviewOGCE Project Overview
OGCE Project Overview
 
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014
Software as a Service workshop / Unlocked: the Hybrid Cloud 12th May 2014
 
Intuit_payment_system_Craft_Demo_slide.pdf
Intuit_payment_system_Craft_Demo_slide.pdfIntuit_payment_system_Craft_Demo_slide.pdf
Intuit_payment_system_Craft_Demo_slide.pdf
 
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023Simplify and Scale Enterprise Apps in the Cloud | Boston 2023
Simplify and Scale Enterprise Apps in the Cloud | Boston 2023
 
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023
Simplify and Scale Enterprise Apps in the Cloud | Seattle 2023
 
Enabling .NET Apps with Monitoring and Management Using Steeltoe
Enabling .NET Apps with Monitoring and Management Using SteeltoeEnabling .NET Apps with Monitoring and Management Using Steeltoe
Enabling .NET Apps with Monitoring and Management Using Steeltoe
 
Odata V4 : The New way to REST for Your Applications
Odata V4 : The New way to REST for Your Applications Odata V4 : The New way to REST for Your Applications
Odata V4 : The New way to REST for Your Applications
 
Micro service architecture
Micro service architectureMicro service architecture
Micro service architecture
 
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...
Building a Global-Scale Multi-Tenant Cloud Platform on AWS and Docker: Lesson...
 
How to Contribute to Apache Usergrid
How to Contribute to Apache UsergridHow to Contribute to Apache Usergrid
How to Contribute to Apache Usergrid
 
Deep Dive: Strategic Importance of BaaS
Deep Dive: Strategic Importance of BaaSDeep Dive: Strategic Importance of BaaS
Deep Dive: Strategic Importance of BaaS
 
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...
PuppetConf 2016: The Long, Twisty Road to Automation: Implementing Puppet at ...
 
Consuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL WebservicesConsuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL Webservices
 

More from Rob Tweed

Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language FeatureRob Tweed
 
LNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooLNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooRob Tweed
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceEWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceRob Tweed
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5Rob Tweed
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4Rob Tweed
 
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3Rob Tweed
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2Rob Tweed
 
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSEWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSRob Tweed
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSEWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSRob Tweed
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDEWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDRob Tweed
 
EWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesRob Tweed
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesRob Tweed
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsEWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsRob Tweed
 

More from Rob Tweed (14)

QEWD Update
QEWD UpdateQEWD Update
QEWD Update
 
Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language Feature
 
LNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooLNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It Too
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceEWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker Appliance
 
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
 
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
 
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSEWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSEWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDEWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
 
EWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database Capabilities
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsEWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
 

Recently uploaded

Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝soniya singh
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsMehedi Hasan Shohan
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 

Recently uploaded (20)

Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Naraina Delhi 💯Call Us 🔝8264348440🔝
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software Solutions
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 

getDemographics = function(message) {// message.path will contain the actual path// message.template will contain the template path// message.parameters will contain the parameter values// ...}Handle the request based on the message details

  • 1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 45 Using QEWD's Advanced MicroService Functionality Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2. Copyright © 2016 M/Gateway Developments Ltd Using QEWD's Advanced MicroService Functionality • 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 • Part 44: Creating MicroServices with QEWD.js
  • 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 Advanced QEWD MicroServices • In this Part of this course, we'll look at the advanced MicroServices features of QEWD, including: – Templated routes – Dynamic path-defined destinations – Federated composite MicroServices from a group of destinations – Re-direction of MicroService responses to another MicroService – Chained MicroServices
  • 12. Copyright © 2016 M/Gateway Developments Ltd Example in Part 44 • In the previous part of this course we set up an example with: – A primary server • One local service: /api/info – A Login MicroService server • One MicroService: /api/login • /api/info could not be run until /api/login had been run, and its returned JWT used in the Authorization Header
  • 13. Copyright © 2016 M/Gateway Developments Ltd Example in Part 44 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
  • 14. Copyright © 2016 M/Gateway Developments Ltd Advanced MicroService Functionality • To demonstrate the advanced MicroService Functionality, we'll extend the examples used in Part 44 • Primary Server: – Startup file: ~/qewd/ms-startup.js – Local REST services: • ~/qewd/node_modules/myLocalServices • Login MicroService Server: – Startup file: ~/qewd/loginservice.js – MicroService Handler Module: • ~/qewd/node_modules/login-micro-service.js
  • 15. Copyright © 2016 M/Gateway Developments Ltd Advanced MicroService Functionality • To demonstrate the advanced MicroService Functionality, we'll extend the examples used in Part 44 • In the example code, I'll assume the host addresses for the servers are: – Primary Server: 192.168.1.120:8080 – Login MicroService Server: 192.168.1.121:8080 • Adjust these to match your servers
  • 16. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • In part 44, the login MicroService route was defined in the Primary Server's startup file using an absolute route: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } };
  • 17. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • Just as with QEWD's REST routing, your MicroService Routes can define variable components, eg: routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login-micro-service' } ]
  • 18. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • Templated Routes can contain any number of variable parts, eg: routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'login-micro-service' } ]
  • 19. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • The message sent to the MicroService will contain: – The actual path that was used – The path template that it matched – The values of the variable path components, as name/value pairs
  • 20. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  • 21. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is • and the actual request was: – /api/patient/123456/headings/allergies/info routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  • 22. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is • and the actual request was: – /api/patient/123456/headings/allergies/info • Message to MicroService will include: – Path: /api/patient/123456/headings/allergies/info – Matched Template Path: /api/patient/:patientId/headings/:headingName/info – Parameters: {patientId: 123456, headingName: 'allergies'} routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  • 23. Copyright © 2016 M/Gateway Developments Ltd Example primary Server Startup File (1) 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; Note that both routes have the same destination
  • 24. Copyright © 2016 M/Gateway Developments Ltd Example primary Server Startup File (2) var routes = [ { path: '/api', module: 'myLocalServices', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes);
  • 25. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server • Define the templated route in your Worker Process Handler module, eg – ~/qewd/node_modules/login-micro-service.js
  • 26. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server • Define the templated route in your Worker Process Handler module, eg – ~/qewd/node_modules/login-micro-service.js init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } This is what we'd created in Part 44 when we defined the login MicroService
  • 27. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } Add the new templated route definition
  • 28. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } Specify the route template exactly the same as on the primary server
  • 29. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } So how will the handler function – getDemographics() in our example here – handle the variable path?
  • 30. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server function getDemographics(args, finished) { var patientId = args.patientId; // ..etc } The values of variable path components are automatically made available to you as properties of the args object It's identical to handling variable routes in standard QEWD REST services (see Part 31)
  • 31. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server var patients = { '123456': { firstName: 'Rob', lastName: 'Tweed', gender: 'Male', country: 'UK' }, '123457': { firstName: 'Jane', lastName: 'Smith', gender: 'Female', country: 'USA' }, }; function getDemographics(args, finished) { var patientId = args.patientId.toString(); if (!patientId || patientId === '') { return finished({error: 'You must specify a patientId'}); } if (!patients[patientId]) { return finished({error: 'Invalid patientId'}); } finished(patients[patientId]); } For example: hard-coded simulation of a patient database
  • 32. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (1) 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'}); } }
  • 33. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (2) var patients = { '123456': { firstName: 'Rob', lastName: 'Tweed', gender: 'Male', country: 'UK' }, '123457': { firstName: 'Jane', lastName: 'Smith', gender: 'Female', country: 'USA' }, }; function getDemographics(args, finished) { var patientId = args.patientId.toString(); if (!patientId || patientId === '') { return finished({error: 'You must specify a patientId'}); } if (!patients[patientId]) { return finished({error: 'Invalid patientId'}); } finished(patients[patientId]); }
  • 34. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } };
  • 35. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/patient/1234567/demographics
  • 36. Copyright © 2016 M/Gateway Developments Ltd Try it
  • 37. Copyright © 2016 M/Gateway Developments Ltd Try it It successfully returned the results for patient 123457 Try changing the patientId in the path to 123456
  • 38. Copyright © 2016 M/Gateway Developments Ltd Security Issue • We have a problem – the demographics MicroService can be invoked by anyone • It should only be accessible if you've logged in using the Login MicroService • We can fix this by adding a beforeMicroServiceHandler() function into the MicroService module
  • 39. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } };
  • 40. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } }; Ignore Login requests – no JWT needed for them
  • 41. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } }; This is the same API that we used in the previous Part of the course. There we applied it to the beforeHandler() function of the Local REST Service Module
  • 42. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/patient/1234567/demographics • Now you should get an error: • Authorization Header missing or JWT not found in header (expected format: Bearer {{JWT}}
  • 43. Copyright © 2016 M/Gateway Developments Ltd Try it • OK so try logging in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  • 44. Copyright © 2016 M/Gateway Developments Ltd Try it • Now try the Demographics request again • In your REST Client: – GET /api/patient/1234567/demographics • Now it should work • Only logged in users can invoke the demographics request
  • 45. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/headings/:heading/summary ': { GET: getHeadingSummary } }; router.addMicroServiceHandler(routes, module.exports); } A path can include as many variable components as you require. Here, we have :patientId and :heading
  • 46. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server function getHeadingSummary(args, finished) { var patientId = args.patientId; var heading = args.heading; // ..etc } The values of variable path components are automatically made available to you as properties of the args object
  • 47. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes
  • 48. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • So far, our examples have used fixed destinations: – A URL route is tied to a specific destination, eg: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] }
  • 49. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • You can also have dynamic routes • :destination is a reserved URL path component name • It's mapped at run-time to the destination actually specified in the URL, and the request is routed to that physical destination
  • 50. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • For example, suppose we have several stores, and we want to retrieve a list of items in stock at a particular store
  • 51. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Define the store destinations in the primary server's startup configuration: u_services: { destinations: { store1: { host: 'http://store1.retailer.com:8080', application: 'stock-list' }, store2: { host: 'http://store2.retailer.com:8080', application: 'stock-list' }, store3: { host: 'http://store3.retailer.com:8080', application: 'stock-list' } } }
  • 52. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Then define the route with the variable destination: u_services: { destinations: { // }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET' } ] }
  • 53. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Each store would have the stock-list application module installed: var router = require('qewd-router'); var routes; function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { stock: 'stock list here...' }; finished(stockListObj);} module.exports = { init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); } };
  • 54. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Each store would have the stock-list application module installed: var router = require('qewd-router'); var routes; function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { stock: 'stock list here...' }; finished(stockListObj);} module.exports = { init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); } }; URL path must be specified identically to primary server's route
  • 55. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • The client then specifies the URL: – GET /api/store/store2/stocklist – In this case, the request will be routed to the destination named store2
  • 56. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • The URL route can include other variable components, eg • The handler function getStockListByCategory() would access the category using args.category routes = { '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, }
  • 57. Copyright © 2016 M/Gateway Developments Ltd Adding Security • Our earlier example included a Login MicroService on a server specifically for user authentication • We've now added 3 stores as additional locations • We'll want to make sure that the stores can't be interrogated until the user has logged in
  • 58. Copyright © 2016 M/Gateway Developments Ltd Adding Security • Just add the beforeMicroServiceHandler() function to the handler module in each of the stores: beforeMicroServiceHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Every incoming request to each store will now need a valid JWT that originated from a valid Login
  • 59. 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 Expre ss Nod e.js socke t.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 Store 1 Store 2 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 ewd-qoper8 queue Express Node.j s socket.i o 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 Store 3
  • 60. Copyright © 2016 M/Gateway Developments Ltd Grouped Destinations
  • 61. Copyright © 2016 M/Gateway Developments Ltd Grouped Destinations • In the previous example, you requested the stock list from a single store that you specified • What if you wanted to get a combined stock listing from all stores? • That's where Grouped Destinations can be used
  • 62. Copyright © 2016 M/Gateway Developments Ltd Setting up a Group Destination • On the primary server: – Define the destination and any routes using the group destination • On the servers within the group destination: – Define the route(s) within the application module
  • 63. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] }
  • 64. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } We're defining a new destination named allStores which is a group consisting of store1 and store2, each of which must be separately defined
  • 65. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } We're defining a new destination named allStores which is a group consisting of store1 and store2, each of which must be separately defined
  • 66. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Now define any routes for this group: u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] }
  • 67. Copyright © 2016 M/Gateway Developments Ltd On each Server in the Group • Define route(s) in the application module init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList }, '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, '/api/all/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); },
  • 68. Copyright © 2016 M/Gateway Developments Ltd On each Server in the Group • Define route(s) in the application module init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList }, '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, '/api/all/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); }, In this example, we'll just re-use the getStockList() function that we previously defined
  • 69. Copyright © 2016 M/Gateway Developments Ltd Security / Authentication • We've already added the beforeMicroServiceHandler() function, so you'll have to first login before you can use the new Grouped destination request. beforeMicroServiceHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); }
  • 70. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/all/stocklist • You should see activity on all machines, but you should get an error: • Authorization Header missing or JWT not found in header (expected format: Bearer {{JWT}}
  • 71. Copyright © 2016 M/Gateway Developments Ltd Try it • OK so try logging in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  • 72. Copyright © 2016 M/Gateway Developments Ltd Try it • Now try again: – GET /api/all/stocklist • This time you should get a composite result back from your store MicroServices – Remember that we asked to run the same getStockList function as before on each store MicroService
  • 73. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 74. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 75. Copyright © 2016 M/Gateway Developments Ltd Try It Each store's results appears within a results object, identified by each destination's name
  • 76. Copyright © 2016 M/Gateway Developments Ltd Try It Response format for all Grouped destinations {results: destination1: {results object}, destination2: {results object}, …etc }
  • 77. Copyright © 2016 M/Gateway Developments Ltd Try It A new JWT with updated expiry is also returned
  • 78. Copyright © 2016 M/Gateway Developments Ltd Dynamic Grouped Destination? u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } Our grouped destination – allStores – is still just a destination.
  • 79. Copyright © 2016 M/Gateway Developments Ltd Dynamic Grouped Destination? u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } So what happens if we use it for this route?
  • 80. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 81. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 82. Copyright © 2016 M/Gateway Developments Ltd Try It Almost identical results as before! A composite result from each destination within the group Is returned
  • 83. Copyright © 2016 M/Gateway Developments Ltd Try It If you look carefully, there is a small difference. This time each store returns a store property with a value of allStores.
  • 84. Copyright © 2016 M/Gateway Developments Ltd Try It That's because the getStockList() function that ran on each store returned the value of args.destination function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { store: args.destination, ip: '192.168.1.119', stock: 'stock list here...' }; finished(stockListObj); }
  • 85. Copyright © 2016 M/Gateway Developments Ltd Try It That's because the getStockList() function that ran on each store returned the value of args.destination :destination was defined in the dynamic route that we just used, but not in the /api/all/stocklist route we used previously function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { store: args.destination, ip: '192.168.1.119', stock: 'stock list here...' }; finished(stockListObj); }
  • 86. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling
  • 87. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client
  • 88. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client • What if you want to do something with the response before it's returned?
  • 89. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client • What if you want to do something with the response before it's returned, eg: – Re-packaging / re-formatting of the response – Conditional behaviour depending on the response content – Triggering a new request to another MicroService
  • 90. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • Each route that you define in your Primary server's startup file can have an optional additional property: – onResponse
  • 91. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • Each route that you define in your Primary server's startup file can have an optional additional property: – onResponse • You define a function to handle the response • The function has a single argument, args, that contains all you need to handle the response
  • 92. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Here's our routes as defined in the Primary Server's startup file
  • 93. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } We'll add custom handling to this one
  • 94. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { console.log('** onResponse: ' + JSON.stringify(args)); } }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Let's initially just inspect that args object
  • 95. Copyright © 2016 M/Gateway Developments Ltd Try it • First log in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  • 96. Copyright © 2016 M/Gateway Developments Ltd Try it • Now send this request: – GET /api/store/store1/stocklist • Now look in the Node.js console log for your Primary server
  • 97. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object received: {"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2 YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ 0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3 MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul- VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"} ** onResponse: {"message":{"type":"ewd-qoper8-express","path":"/api/store/store1/stocklist","method":"GET","headers": {"host":"192.168.1.120:8080","authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NDQsImlhdCI6MTUwNDA4MzI0NCwiaXNzIjoicWV3ZC5 qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzh hZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2Y zU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ0 YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3M TVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.8pY7HEBIdhWZEXR0WBeIJBGEgn-WpRwcwDZf-0uJ0AM","content- type":"application/json"},"params":{"0":"store1/stocklist","type":"store"},"query":{},"body":{},"ip":"::ffff:192.168.1.74","ips": [],"application":"api","expressType":"store"},"destination":"store1","responseObj": {"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2 YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ 0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3 MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul- VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"}}
  • 98. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object{ "message": { "type": "ewd-qoper8-express", "path": "/api/store/store1/stocklist", "method": "GET", "headers": { "host": "192.168.1.120:8080", "authorization": "Bearer eyJ0eXAiOiJKV1..", "content-type": "application/json" }, "params": { "0": "store1/stocklist", "type": "store" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "store" }, "destination": "store1", "responseObj": { "type": "restRequest", "finished": true, "message": { "store": "store1", "ip": "192.168.1.114", "stock": "stock list here...", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..." }, "responseTime": "8ms" } } Let's prettify the args Object, so we can see what it contains
  • 99. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "message": { "type": "ewd-qoper8-express", "path": "/api/store/store1/stocklist", "method": "GET", "headers": { "host": "192.168.1.120:8080", "authorization": "Bearer eyJ0eXAiOiJKV1..", "content-type": "application/json" }, "params": { "0": "store1/stocklist", "type": "store" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "store" }, args.message: -The original incoming request object
  • 100. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "destination": "store1", } args.destination: -Any variable path components will be available as properties in their own right. So the value of :destination in our request is available to us
  • 101. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "responseObj": { "type": "restRequest", "finished": true, "message": { "store": "store1", "ip": "192.168.1.114", "stock": "stock list here...", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..." }, "responseTime": "8ms" } } args.responseObj: -The raw response object that was returned from the MicroService
  • 102. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send
  • 103. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse • If you want to re-package / re-format the response, you must use this function to return your response to the REST client. – args.send
  • 104. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send • If you want to send a request to another MicroService, you use this function
  • 105. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send • Note: if you use either of these functions, your onResponse() function MUST return true. This tells QEWD not to try to handle the response itself
  • 106. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { console.log('** onResponse: ' + JSON.stringify(args)); } }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Let's return our own custom response…
  • 107. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] }
  • 108. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } If an error has occurred, we'll let QEWD handle it
  • 109. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } Otherwise, we'll create our own response object from information in the args object
  • 110. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } Use the handleResponse() function to return the response to the REST client
  • 111. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } And finally tell QEWD that we've handled the response ourselves, so it doesn't need to
  • 112. Copyright © 2016 M/Gateway Developments Ltd Try It • Remember to first log in
  • 113. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 114. Copyright © 2016 M/Gateway Developments Ltd Try It There's our re-packaged custom response
  • 115. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } One thing to remember: Your onResponse() function is invoked in the QEWD Master process
  • 116. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } So make sure you don't do anything CPU-intensive or long-running
  • 117. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } You should consider running your logic in a QEWD worker process. If you do, you'd also have access to the database
  • 118. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } But how do you use a QEWD Worker Process from here? It's actually very easy…
  • 119. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true }; } } } ] } First we create a message object It must contain at least these 3 properties. The value of type is up to you, but use the values shown for token and jwt
  • 120. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; } } } ] } Now we'll add the data we want to send to the Worker in our message
  • 121. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); } } } ] } this.handleMessage() is the ewd-qoper8 API for sending a message to a Worker. Its callback allows us to process the response from the Worker – we'll send the response to the REST client, using args.handleResponse()
  • 122. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } Finally, tell QEWD that we're Handling the response
  • 123. Copyright © 2016 M/Gateway Developments Ltd Try It • Login and put the JWT into the Authorization header as before • Then send: – GET /api/store/store1/stocklist • You should get back an error: {"error": "Unable to load handler module for: stock-list"}
  • 124. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it
  • 125. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it • But the error was because it couldn't find a module named stock-list – Why was it looking for a module of this name?
  • 126. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it • But the error was because it couldn't find a module named stock-list – Why was it looking for a module of this name? • It's because we sent the JWT as the message token
  • 127. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } We sent the JWT that was returned by the MicroService
  • 128. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { console.log('** token: ' + args.responseObj.message.token); var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } We'll display the token in the Node.js console log
  • 129. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT • Restart QEWD on the Primary server and try sending that same request • Then take a look in the Node.js console log
  • 130. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT ** token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwOTQwNjcsImlhd CI6MTUwNDA5Mjg2NywiaXNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0 b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzhhZjF mOWI4ZTFmNTFkNjNkN2FlNmM3ZjAzZjg0MmJjYjQ0OWNkZmU4Mjc4YTY5O Dc5NmE3ODAwMGJkZjJmNDIxZWU2YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1Yj RiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2 YTQ0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZ DM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3MTVkIiwidXNlclRleHQiOiJXZ Wxjb21lIFJvYiJ9.2xGoGnrdFjMf6rn5GEkOAlHamFPceQC6jiS4M6siPb0 Wed, 30 Aug 2017 11:34:27 GMT; worker 13037 received message: {"type":"save","params": {"path":"/api/store/store1/stocklist","method":"GET","store":"store1","stock":"stock list
  • 131. Copyright © 2016 M/Gateway Developments Ltd Inspect the JWT using jwt.io
  • 132. Copyright © 2016 M/Gateway Developments Ltd Inspect the JWT using jwt.io This is why the QEWD Worker Is looking for a module named stock-list
  • 133. Copyright © 2016 M/Gateway Developments Ltd Let's create that module • On your Primary server, create a new text file: – ~/qewd/node_modules/stock-list.js
  • 134. Copyright © 2016 M/Gateway Developments Ltd Let's create that module • On your Primary server, create a new text file: – ~/qewd/node_modules/stock-list.js module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } };
  • 135. Copyright © 2016 M/Gateway Developments Ltd Let's create that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } }; For the purpose of this example/demo, we're just going to send the incoming message back to the Master process
  • 136. Copyright © 2016 M/Gateway Developments Ltd Let's create that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } }; But so that we can be sure that a Worker really handled it, we'll add this to the message
  • 137. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD on the Primary Server • Login again and copy the JWT to the Authorization Header • Then send: – GET /api/store/store1/stocklist
  • 138. Copyright © 2016 M/Gateway Developments Ltd Try It You should see a response similar to this
  • 139. Copyright © 2016 M/Gateway Developments Ltd Try It You can see it's the message we sent to the Worker for processing
  • 140. Copyright © 2016 M/Gateway Developments Ltd Try It Here's the additional property added by our Worker module's handler function
  • 141. Copyright © 2016 M/Gateway Developments Ltd By running in a Worker • You have access to the integrated QEWD database • You have access to the Session data contained within the JWT
  • 142. Copyright © 2016 M/Gateway Developments Ltd Let's enhance that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); finished(messageObj); } } }; For example, we could save the message object to the database
  • 143. Copyright © 2016 M/Gateway Developments Ltd Let's enhance that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } }; And we could add a new data item to the Session / JWT
  • 144. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD • Login and copy the JWT to the Authorization Header as before • Send: – GET /api/store/store1/stocklist
  • 145. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 146. Copyright © 2016 M/Gateway Developments Ltd Now Check the Database using qewd-monitor Click the Document Store Tab
  • 147. Copyright © 2016 M/Gateway Developments Ltd Now Check the Database using qewd-monitor Here's the saved Document we created, containing the message we sent to the Worker
  • 148. Copyright © 2016 M/Gateway Developments Ltd Reminder of how that happened module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } };
  • 149. Copyright © 2016 M/Gateway Developments Ltd Now Inspect the JWT Copy this token and paste it into the jwt.io inspector
  • 150. Copyright © 2016 M/Gateway Developments Ltd Now Inspect the JWT Here's the field we added to the Session and therefore to the JWT
  • 151. Copyright © 2016 M/Gateway Developments Ltd Reminder of how that happened module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } };
  • 152. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far you've learned how to: – re-package / re-format the response from a MicroService – Process the response from a MicroService, including: • saving some or all of it to the QEWD database • Modifying the JWT before returning the response to the REST client • You should be able to adapt the examples to make the behaviour conditional on the response content
  • 153. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • What if you wanted to trigger a new request to another MicroService, eg: – Make the /api/login request to the Primary Server: • Invoke the call to the authentication MicroService to log us in (using the username and password credentials), as we've done already • But then, if successful, make a call to a demographics MicroService to get some basic information about the logged-in user • Add those demographic details to the response that is returned to the REST client
  • 154. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse – args.send • If you want to send a request to another MicroService, you use this function
  • 155. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse • We've already seen how to use this to return our customised response back to the REST client – args.send
  • 156. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse – args.send • Now we'll use this function, which allows us to send a new request to a MicroService
  • 157. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; This is the startup file for the Primary server
  • 158. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; The demographics route was described in detail at the start of this Part of the course
  • 159. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; For this example, both routes are handled by the same destination, but they could be at different destinations
  • 160. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; The demographics route expects a variable value – the patientId Let's first change the login MicroService so it returns an id that we can use for the demographics route
  • 161. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService • On your Login MicroService machine, find the handler module: – ~/qewd/node_modules/login-micro-service.js – We'll edit the login() function
  • 162. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService 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.userId = 123456; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); return finished({ok: true}); } else { return finished({error: 'Invalid login'}); } } Add this line For simplicity, in this Example, we're just hard-coding an Id In the real-world we'd probably do some kind of database lookup
  • 163. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService • What this modification will do is add the userId to the JWT that is returned to the Primary server • Stop and restart the Login MicroService and try logging in again
  • 164. Copyright © 2016 M/Gateway Developments Ltd Modified Login MicroService Response So the Login MicroService returns 2 properties: ok and token Let's copy and paste the token and inspect it using jwt.io
  • 165. Copyright © 2016 M/Gateway Developments Ltd Modified Login MicroService Response So here's the new userId field that we added
  • 166. Copyright © 2016 M/Gateway Developments Ltd Custom Login Response Handler • We now need to go back to the startup file on your Primary Server, and add an onResponse() handler function to the Login route definition
  • 167. Copyright © 2016 M/Gateway Developments Ltd Primary Server Startup File 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.121:8080', application: 'login-micro-service', onResponse: function(args) { } } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; We'll zoom in here on the next slides..
  • 168. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } },
  • 169. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We'll only make a call to the demographics service if the login was successful
  • 170. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Use this.jwt.handlers.getProperty() to extract a public claim from the JWT – in this case the userId This doesn't require the secret, so is lightweight enough to perform in the master process
  • 171. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The first argument specifies the Property or Claim name we want
  • 172. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The 2nd argument is a JWT. We'll use the JWT returned in the Login MicroService response
  • 173. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Next, we construct a minimal message object to send a demographics MicroService request
  • 174. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The path will match that of the demographics route We add into the path the userId extracted from the Login JWT
  • 175. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The HTTP method we want to use is GET
  • 176. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Finally, we must add the authorization header. The format must be the same as if we'd submitted the request from a REST client We'll use the JWT returned by the Login MicroService
  • 177. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, If the MicroService we want to use had a POST method, we'd need to specify a body object in the message. If the URL needed additional name/value pairs, we'd add a query object to the method
  • 178. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The message object should be sufficient for the demographics MicroService, so now we send it using the args.send() function
  • 179. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We want to combine the response from the Login MicroService with what comes back from the Demographics MicroService, so we'll do that in the arg,send callback function
  • 180. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We'll get the ok property from the Login response In other situations you might have many more properties to merge into the final response
  • 181. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, And now we're ready to return the composite response back to the REST client. We use args.handleResponse() for this, as before
  • 182. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, One last very important step – we must tell QEWD that we're handling the response
  • 183. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD on the Primary and Login MicroService servers • Send: – GET /api/login • With the appropriate body payload containing the username and password
  • 184. Copyright © 2016 M/Gateway Developments Ltd Try It
  • 185. Copyright © 2016 M/Gateway Developments Ltd Try It Success!
  • 186. Copyright © 2016 M/Gateway Developments Ltd Try It These properties came from the Demographics service
  • 187. Copyright © 2016 M/Gateway Developments Ltd Try It And this came from the Login service
  • 188. Copyright © 2016 M/Gateway Developments Ltd Recap so far • You've now learned how to: – Use templated routes with variable path components – Add custom response handling • And optionally perform some or all of that handling in a QEWD worker process, allowing: – use of the integrated QEWD document database – The JWT contents to be manipulated • And optionally send one or more further requests to MicroServices and construct a composite response
  • 189. Copyright © 2016 M/Gateway Developments Ltd All on the Primary Server • Everything we've done so far in this Part of the Course has been on the Primary Server
  • 190. 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 & demographics 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 Request
  • 191. 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 & demographics 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 Request
  • 192. 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 & demographics 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 Response
  • 193. 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 & demographics 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 Demographics Request
  • 194. 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 & demographics 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 WebSocket Connections Demographics Response
  • 195. 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 & demographics 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 Composite Response
  • 196. Copyright © 2016 M/Gateway Developments Ltd What about the MicroService Servers? • Is it possible for a MicroService to make requests to other MicroServices? – In other words…
  • 197. 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 User authentication Demographics 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 Request
  • 198. 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 User authentication Demographics 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 Request
  • 199. 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 User authentication Demographics 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 Demographics Request
  • 200. 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 User authentication Demographics 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 Demographics Response
  • 201. 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 User authentication Demographics 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 Response
  • 202. 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 User authentication Demographics 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 Response
  • 203. Copyright © 2016 M/Gateway Developments Ltd Lets build out the 3 servers • Primary Server – Will handle the /api/login request sent from the REST client • Login Server – Will handle the /api/login request sent from the Primary Server • Demographics Server – Will handle the /api/patient/:patientId/demographics request sent from the Login Server
  • 204. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Startup File – ~/qewd/primary.js
  • 205. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } };
  • 206. Copyright © 2016 M/Gateway Developments Ltd Primary Server (2) var routes = [ { path: '/api', module: 'localServices' } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes);
  • 207. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) - Notes 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.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } }; The Primary Server only knows the route to the Login Server
  • 208. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) - Notes var config = { managementPassword: 'keepThisSecret!', serverName: 'Primary QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } }; Adjust to match your Login Server location
  • 209. Copyright © 2016 M/Gateway Developments Ltd Primary Server (2) - Notes var routes = [ { path: '/api', module: 'localServices' } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes); Our example is only going to respond to incoming requests for /api/login which it will forward to the Login MicroService server. We won't be handling any other routes locally on this server. Nevertheless, the Primary Server needs to know to recognise any paths starting /api, so we need to specify it as a notional route We'll specify a non-existent local handler module named localServices that won't actually get used
  • 210. Copyright © 2016 M/Gateway Developments Ltd Login Server • Startup file: – ~/qewd/loginserver.js • Worker Module for handling the /api/login login requests • ~/qewd/node_modules/login-micro-service.js
  • 211. Copyright © 2016 M/Gateway Developments Ltd Login Server: Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config);
  • 212. Copyright © 2016 M/Gateway Developments Ltd var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config); The Login Server needs to know the route to the Demographics MicroService Login Server: Startup File
  • 213. Copyright © 2016 M/Gateway Developments Ltd var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config); Adjust to match your Demographics Server location Login Server: Startup File
  • 214. Copyright © 2016 M/Gateway Developments Ltd Login Server: Worker Module (1) 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.userId = 123456; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); return finished({ok: true}); } else { return finished({error: 'Invalid login'}); } } This file is too long to fit on one slide This is part 1 See next slide for the rest of the file
  • 215. Copyright © 2016 M/Gateway Developments Ltd Login Server: Worker Module (2) module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); }, workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } } };
  • 216. Copyright © 2016 M/Gateway Developments Ltd The key new piece module.exports = { init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); }, workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } } };
  • 217. Copyright © 2016 M/Gateway Developments Ltd The key new piece Any QEWD Worker Module can have a workerResponseHandlers object defined. It is used to define handler functions that are invoked in the Master Process for any specified message type, once that message's worker-side handler has finished workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 218. Copyright © 2016 M/Gateway Developments Ltd The key new piece The workerResponseHandlers handler function for a message type is invoked after the main handler function has completed, and before the response is returned to the client. We can use this to intercept the Normal MicroServices flow. workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 219. Copyright © 2016 M/Gateway Developments Ltd The key new piece QEWD MicroServices always use a pre-determined message type of restRequest. So we're defining a workerResponseHander function that will intercept all microService responses. workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 220. Copyright © 2016 M/Gateway Developments Ltd The key new piece So the first thing we must do is filter out the one(s) we're interested in. In fact In the example there will only be one MicroService request type hitting this Code – for /api/login - but you'll often need to know how to do this workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 221. Copyright © 2016 M/Gateway Developments Ltd The key new piece For MicroService messages (ie of type restRequest), QEWD adds the path (actually the path template) to the message argument. So we can filter on this as shown workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 222. Copyright © 2016 M/Gateway Developments Ltd The key new piece We need to know the user Id for the logged in user. So, just as we did earlier, we'll extract it from the JWT. It's in message.token workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }
  • 223. Copyright © 2016 M/Gateway Developments Ltd The key new piece Now we can call out to the demographics MicroService. We create a message object containing, at minimum, the path, method and headers This is just the same as we did before when we called the MicroService from the Primary Server workerResponseHandlers: { restRequest: function(message, send) { if (message.path === '/api/login') { var ok = message.ok; var userId = this.jwt.handlers.getProperty('userId', message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + message.token } }; this.microServiceRouter.call(this, message, function(responseObj) { responseObj.message.ok = ok; send(responseObj); }); return true; } } }