SlideShare a Scribd company logo
Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 31
Using QEWD for Web
and REST Services
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
Using QEWD for Web & REST Services
• This part of the course assumes that
you've taken, at least:
• Part 2: Overview of EWD 3
– https://www.slideshare.net/robtweed/ewd-3-overview
• Part 4: Installing & Configuring QEWD
– https://www.slideshare.net/robtweed/installing-configuring-ewdxpress
Copyright © 2016 M/Gateway Developments Ltd
QEWD for Web & REST Services
• Provides a ready-made platform for
developing high-performance, scalable,
secure Web and REST Service APIs
Copyright © 2016 M/Gateway Developments Ltd
QEWD for Web & REST Services
• Just as for interactive browser-based
applications, the master QEWD process
handles concurrency and HTTP(S) access
• Your APIs run in the isolated environment
of a QEWD worker process
– A QEWD worker process invokes a single
instance of an API function
• So it can safely use synchronous APIs
Copyright © 2016 M/Gateway Developments Ltd
QEWD for Web & REST Services
• An instance of QEWD can simultaneously
support:
– Multiple interactive browser-based
applications; and/or
– Multiple REST / Web Service APIs
Copyright © 2016 M/Gateway Developments Ltd
Typical QEWD Startup file for browser
apps
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'cache',
params: {
path: 'c:InterSystemsCache2015-2mgr’
}
}};
var qewd = require('qewd').master;
qewd.start(config);
If using InterSystems Cache
Copyright © 2016 M/Gateway Developments Ltd
Typical QEWD Startup file for browser
apps
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
};
var qewd = require('qewd').master;
qewd.start(config);
If using GT.M
Copyright © 2016 M/Gateway Developments Ltd
Defining Routes
• To support Web/REST APIs, you must
add routes to the startup configuration:
– Associate a URL prefix with a back-end
QEWD module
– QEWD uses this information and
automatically looks after the Express
middleware and routing needed for incoming
matching URLs to invoke the associated
back-end module in a worker process
Copyright © 2016 M/Gateway Developments Ltd
Defining Routes
• The URL routes you want to handle are
defined in your QEWD startup file:
– An array of routing objects
Copyright © 2016 M/Gateway Developments Ltd
Defining Routes
• The URL routes you want to handle are
defined in your QEWD startup file:
– An array of routing objects
– Each routing object defines:
• path: the URL path prefix
• module: the associated back-end module
name/path
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
All incoming HTTP requests
with a URL prefix of /api
will be handled by a module
named myRestService
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
Pass the routes array
into the QEWD start()
function as its 2nd
argument
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{path: '/api', module: 'myRestService'},
{path: '/testing', module: '/path/to/testService'}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
You can define as many
routes as you wish
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{path: '/api', module: 'myRestService'},
{path: '/testing', module: '/path/to/testService'}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
You can specify a module
path if necessary
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
With this route definition,
all incoming HTTP requests
with a URL prefix of /api
will be handled by a module
named myRestService
Copyright © 2016 M/Gateway Developments Ltd
Add Routes to Startup File
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
Any incoming HTTP requests
with a URL prefix that
doesn't start /api will be
rejected by QEWD
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any
incoming HTTP(S) requests whose URL
starts with a specified route
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will reject any
incoming HTTP(S) requests whose URL
can't be matched in the routes array.
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any incoming
HTTP(S) requests whose URL starts with a
specified route
• Matching requests are repackaged into a JSON
message and placed on queue
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any incoming
HTTP(S) requests whose URL starts with a
specified route
• Matching requests are repackaged into a JSON
message and placed on queue
• Queued message is dispatched to a worker
process
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any incoming
HTTP(S) requests whose URL starts with a
specified route
• Matching requests are repackaged into a JSON
message and placed on queue
• Queued message is dispatched to a worker
process
• Message is handled in the worker process by
the module specified in the route definition
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any
incoming HTTP(S) requests whose URL
starts with a specified route
– Note: matching of routes in the master
process is by exact match of URL prefixes
defined in startup file route definitions
– URL templates are not used in the startup file
route definitions
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• QEWD Master Process will detect any
incoming HTTP(S) requests whose URL
starts with a specified route
– Startup file route definitions are simply a way
to direct a set of incoming requests to a
worker process where they are processed by
a particular handler module
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• In your Worker Process handler module,
you specify all the next-level routes you're
interested in, and the function to handle
each route
• Worker Process routes can be templated,
eg
– /api/customer/:id
Copyright © 2016 M/Gateway Developments Ltd
How QEWD Routes Work
• For example, if the QEWD startup file defines a route for:
– /api
• The worker process module specified for this route will
handle or reject all lower-level URL paths, eg:
– /api/search
– /api/patient/allergy/detail
– /api/user
– /api/customer/123456
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
module.exports = {
restModule: true
};
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
module.exports = {
restModule: true,
};
You must tell QEWD that this
Module is for handling
Web/REST service APIs
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
module.exports = {
restModule: true,
init: function() {
// invoked just once when module is first loaded into worker process
}
};
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
module.exports = {
restModule: true,
init: function() {
// invoked just once when module is first loaded into worker process
// this is where you define all the lower-level routes you want to handle
// - path template
// - HTTP method (eg POST, GET, etc)
// - handler function to process all requests matching this path
}
};
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
module.exports = {
restModule: true,
init: function() {
// invoked just once when module is first loaded into worker process
}
};
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
}
};
Define an array of routes
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
}
};
Each route specifies:
-url template
-method (if not specified, all methods apply)
-handler function
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
function search(args, finished) {
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/test',
method: 'GET',
handler: search
}
]
}
};
Define handler function
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
function search(args, finished) {
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
}
};
Define handler function
Arguments:
-args: object containing request data
-finished: function you use to return response
object and release worker process
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
function search(args, finished) {
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
}
};
Define handler function. It:
•should process the incoming message object
•must create a response object
•must end by invoking finished() function, which:
- returns response object back to client
- releases worker process to available pool
Copyright © 2016 M/Gateway Developments Ltd
Handler Module Structure
var router = require('qewd-router');
var routes;
function search(args, finished) {
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
routes = router.initialise(routes, module.exports);
}
};
Finally, activate the routes
Copyright © 2016 M/Gateway Developments Ltd
Handler Function Structure
var router = require('qewd-router');
var routes;
function search(args, finished) {
console.log('*** search args: ' + JSON.stringify(args, null, 2));
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
routes = router.initialise(routes, module.exports);
}
};
Simple Example:
Copyright © 2016 M/Gateway Developments Ltd
Let's try it out…
Copyright © 2016 M/Gateway Developments Ltd
Create a new QEWD startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'QEWD REST Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{path: '/api', module: 'myRestService'}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
~/qewd/rest.js
Copyright © 2016 M/Gateway Developments Ltd
Create the handler module
var router = require('qewd-router');
var routes;
function search(args, finished) {
console.log('*** search args: ' + JSON.stringify(args, null, 2));
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
routes = router.initialise(routes, module.exports);
}
};
~/qewd/node_modules/myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Start QEWD using your startup file
cd ~/qewd
node rest
Copyright © 2016 M/Gateway Developments Ltd
Try it
It works!
Copyright © 2016 M/Gateway Developments Ltd
Check in the QEWD Node log:
*** search args: {
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
Our search() function displayed the entire
args object to the console log:
Copyright © 2016 M/Gateway Developments Ltd
Check in the QEWD Node log:
*** search args: {
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
function search(args, finished) {
console.log('*** search args: ' + JSON.stringify(args, null, 2));
finished({test: 'finished ok'});
}
Our search() function displayed the entire
messageObj object to the console log:
Copyright © 2016 M/Gateway Developments Ltd
Your API handler function
• So what your QEWD Web/REST Service
API handler functions must do is process
the incoming message/request object:
– To determine what to do
– And use appropriate information in the
incoming request object to:
• Fetch data
• Save data
• Change data
• …etc
Copyright © 2016 M/Gateway Developments Ltd
Your API handler function
• It's therefore vital to understand what's in
the args object that's made available by
QEWD's router to your handler functions.
• Let's take a detailed look…
Copyright © 2016 M/Gateway Developments Ltd
Check in the QEWD Node log:
*** search args: {
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
All the elements of the
incoming HTTP request
have been broken out
and made available
in the incoming args
Object
That was done automatically
for us by the QEWD router
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
Request Type
ie 2nd
-level URL path:
/api/search
- args.req.params.type
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The full URL path
•args.req.path
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The HTTP method
used to submit the
Request
- args.req.method
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chro..",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The HTTP headers
sent with the request
- args.req.headers
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The HTTP URL query string (if any)
This is where you'll pick up any
name/value pairs that were added
to the URL
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search?name=smith",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {
name: 'smith'
},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The HTTP URL query string (if any)
This is where you'll pick up any
name/value pairs that were added
to the URL, eg
/api/search?name=smith
- args.req.query.name
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search?name=smith",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {
name: 'smith'
},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
Note args.req.path includes
the query string too
/api/search?name=smith
- args.req.query.name
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The HTTP request body payload (if
any). If the HTTP request was sent as
an application/json type, body will
contain the pre-parsed JSON
Only applies for POST / PUT methods
- args.req.body
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "GET",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
The IP address of the client
endpoint that sent the request
- args.req.ip
Copyright © 2016 M/Gateway Developments Ltd
Use a proper HTTP Client
• A browser will only allow us to test GET
requests
– Need to test POST and other requests too (eg
DELETE, PUT)
• Numerous web service/REST clients
available
– Postman
– Chrome's Advanced REST Client (ARC)
Copyright © 2016 M/Gateway Developments Ltd
Try a POST request
Copyright © 2016 M/Gateway Developments Ltd
Why did it error?
var router = require('qewd-router');
var routes;
function search(args, finished) {
console.log('*** search args: ' + JSON.stringify(args, null, 2));
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
}
]
routes = router.initialise(routes, module.exports);
}
};
~/qewd/node_modules/myRestService.js
We're specifically only
handling the GET method
Copyright © 2016 M/Gateway Developments Ltd
To handle all methods:
var router = require('qewd-router');
var routes;
function search(args, finished) {
console.log('*** search args: ' + JSON.stringify(args, null, 2));
finished({test: 'finished ok'});
}
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
handler: search
}
]
routes = router.initialise(routes, module.exports);
}
};
~/qewd/node_modules/myRestService.js
Don't specify method at all
Copyright © 2016 M/Gateway Developments Ltd
Try Again
• Stop and restart QEWD
– To make sure the new version of the handler
module is loaded into the worker process that
handles the request
• Alternatively, use the qewd-monitor
application and stop all worker processes
Copyright © 2016 M/Gateway Developments Ltd
Try the POST request again
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "POST",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
Copyright © 2016 M/Gateway Developments Ltd
Try a POST with JSON payload
Copyright © 2016 M/Gateway Developments Ltd
QEWD args Object{
"req": {
"type": "ewd-qoper8-express",
"path": "/api/search",
"method": "POST",
"headers": {
"host": "192.168.1.117:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.8",
"cookie": "io=5rWbmzRiCtkbJraIAAAA",
"if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U""
},
"params": {
"type": "search"
},
"query": {},
"body": {
"name": "rob"
},
"ip": "::ffff:192.168.1.74",
"ips": [],
"application": "api",
"expressType": "search"
},
"session": {}
}
Here's the body payload
- args.req.body.name
Copyright © 2016 M/Gateway Developments Ltd
Using Path Templates
• The routes that you specify in your worker
process handler module can include
templates
– Variable pieces of the URL path are prefixed
with a colon
• /api/search/:text
• This would match, eg:
– /api/search/hello
– /api/search/world
Copyright © 2016 M/Gateway Developments Ltd
Using Path Templates
• Variable pieces of the URL path are
prefixed with a colon
• /api/search/:text
• The variable name (eg text) is made
available in the args object
– eg:
• /api/search/hello args.text = 'hello'
• /api/search/world args.text = 'world'
Copyright © 2016 M/Gateway Developments Ltd
Using Path Templates
• You can have as many variables as you
wish in a URL path template, eg
• /api/patient/:patientId/:heading/detail/:sourceId
• In args we'd find:
– args.patientId
– args.heading
– args.sourceId
Copyright © 2016 M/Gateway Developments Ltd
Example: add new route to:
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
}
]
routes = router.initialise(routes, module.exports);
}
};
~/qewd/node_modules/myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Add the getSummary function to
var headings = {
allergies: true,
medications: true
};
function getSummary(args, finished) {
if (headings[args.heading]) {
finished({
patient: args.patientId,
heading: args.heading,
summary: 'details would be here'
});
}
else {
finished({error: 'Invalid heading ' + args.heading});
}
}
~/qewd/node_modules/myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Try Again
• Stop and restart QEWD
– To make sure the new version of the handler
module is loaded into the worker process that
handles the request
• Alternatively, use the qewd-monitor
application and stop all worker processes
Copyright © 2016 M/Gateway Developments Ltd
We'll try the new route
• With this URL:
– GET /api/patient/123456/allergies/summary
Copyright © 2016 M/Gateway Developments Ltd
Try the new route
Copyright © 2016 M/Gateway Developments Ltd
Try an invalid heading value
Copyright © 2016 M/Gateway Developments Ltd
Writing a QEWD Web/REST API
• In your worker process handler module, simply
define all the URL routes you want to allow, and
write a corresponding handler function for each
one
• What the handler functions actually do is up to
you
• All the information your functions require will be
in the args object
• Each of your functions must create a response
object and return it using finished() function
Copyright © 2016 M/Gateway Developments Ltd
Handling Errors
• Return a response object that contains an
error property
– Its value should be the error string you want
returned
• {error: 'My error message'}
– By default, the HTTP status for an error will be
400
Copyright © 2016 M/Gateway Developments Ltd
Remember our last error:
Copyright © 2016 M/Gateway Developments Ltd
Controlling the Error status
function getSummary(args, finished) {
if (headings[args.heading]) {
finished({
patient: args.patientId,
heading: args.heading,
summary: 'details would be here'
});
}
else {
finished({
error: 'Invalid heading ' + args.heading,
status: {
code: 401
}
});
}
}
~/qewd/node_modules/myRestService.js
Define a custom status code
value in the error response
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and try again:
Copyright © 2016 M/Gateway Developments Ltd
Other Errors
• Invalid routes
– ie ones that aren't defined in the routes array
Copyright © 2016 M/Gateway Developments Ltd
Specify the error response:
module.exports = {
restModule: true,
init: function() {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
}
]
routes = router.initialise(routes, module.exports);
router.setErrorResponse(404, 'Not Found');
}
};
~/qewd/node_modules/myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD. Try an invalid path:
Copyright © 2016 M/Gateway Developments Ltd
Why doesn't this work?
Copyright © 2016 M/Gateway Developments Ltd
It's due to QEWD's handler logic
• 2nd
-level URL paths are defined behind the
scenes with a handler function
– So in our example there's one for /api/search
– And one for /api/patient
– router.setErrorResponse() handles "not
found" errors within those paths
– But /api/xxxxxx isn't defined as a route so
router.setErrorResponse() can't handle it as
an error
Copyright © 2016 M/Gateway Developments Ltd
Requires an additional step
• this.setCustomErrorResponse()
Copyright © 2016 M/Gateway Developments Ltd
Specify the error response:
module.exports = {
restModule: true,
init: function(application) {
routes = [
{
url: '/api/search',
method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
}
]
routes = router.initialise(routes, module.exports);
router.setErrorResponse(404, 'Not Found');
}
};
~/qewd/node_modules/myRestService.js
First, add application
as an argument to the
init() function
Copyright © 2016 M/Gateway Developments Ltd
Specify the error response:
]
routes = router.initialise(routes, module.exports);
router.setErrorResponse(404, 'Not Found');
this.setCustomErrorResponse.call(this, {
application: application,
errorType: 'noTypeHandler',
text: 'Resource Not Found',
statusCode: '404'
});
}
};
~/qewd/node_modules/myRestService.js
Then add the setCustomErrorResponse()
Function within the init() function, exactly
as shown here
Copyright © 2016 M/Gateway Developments Ltd
Now it should work
Copyright © 2016 M/Gateway Developments Ltd
Writing a QEWD Web/REST API
• Within your handler functions, you have
access to the embedded database via
– this.db
• eg for this.db.function() calls to legacy code
– this.documentStore
• This is what you'll usually use for database access
– Also now abstracted via this.db.use()
Copyright © 2016 M/Gateway Developments Ltd
Background on QEWD's Embedded Database
• See Training Course:
– http://docs.qewdjs.com/qewd_training.html
– Parts 17 – 26 for details on the embedded
Document Store used by QEWD
Copyright © 2016 M/Gateway Developments Ltd
Example: /api/save handler
routes = [
{
url: '/api/search',
//method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
},
{
url: '/api/save',
method: 'POST',
handler: save
}
]
routes = router.initialise(routes, module.exports);
Copyright © 2016 M/Gateway Developments Ltd
Example: /api/save handler
function save(args, finished) {
var doc = new this.documentStore.DocumentNode('myData');
var ix = doc.increment();
doc.$(ix).setDocument(args.req.body);
finished({saved: ix});
}
Copyright © 2016 M/Gateway Developments Ltd
Example: /api/save handler
function save(args, finished) {
var doc = this.db.use('myData');
var ix = doc.increment();
doc.$(ix).setDocument(args.req.body);
finished({saved: ix});
}
Can now also use
this syntax
Copyright © 2016 M/Gateway Developments Ltd
Example: /api/save handler
function save(args, finished) {
var doc = this.db.use('myData');
var ix = doc.increment();
doc.$(ix).setDocument(args.req.body);
finished({saved: ix});
}
This will save the POSTed body JSON into a
persistent document named myData
Copyright © 2016 M/Gateway Developments Ltd
Example: /api/save handler
function save(args, finished) {
var doc = this.db.use('myData');
var ix = doc.increment();
doc.$(ix).setDocument(args.req.body);
finished({saved: ix});
}
Each time you invoke it, it will increment the
database index
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and try it
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
• qewd-monitor is a WebSocket-based
browser application that is installed
automatically if you use one of the QEWD
installer scripts
– See:
– https://www.slideshare.net/robtweed/installing-configuring-ewdxpress
– https://github.com/robtweed/qewd/tree/master/installers
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
• start qewd-monitor in a browser using the
URL
– http://192.168.1.119:8080/qewd-monitor/index.html
• Change the IP address / port to match your QEWD
configuration
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
• Log in to qewd-monitor with the
managementPassword you specified in
your QEWD startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
• Log in to qewd-monitor with the
managementPassword you specified in
your QEWD startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
}
};
var routes = [
{
path: '/api',
module: 'myRestService'
}
];
var qewd = require('qewd').master;
qewd.start(config, routes);
It's a good idea to change
this password from the
default!
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
Then click on the
Document Store tab
Copyright © 2016 M/Gateway Developments Ltd
Check the database using qewd-monitor
And sure enough, here's
our POSTed data stored
under index 1
Copyright © 2016 M/Gateway Developments Ltd
QEWD Multi-tasking
• Note that your instance of QEWD.js is not
only supporting your REST services
• It's also simultaneously supporting a
WebSocket-based browser application
Copyright © 2016 M/Gateway Developments Ltd
QEWD Multi-tasking
• Note that your instance of QEWD.js is not
only supporting your REST services
• It's also simultaneously supporting a
WebSocket-based browser application
• A single instance of QEWD can
simultaneously support as many browser
applications and REST services as you
wish
Copyright © 2016 M/Gateway Developments Ltd
No QEWD Session?
• If you're familiar with QEWD browser-
based applications, you'll know that they
automatically have a QEWD Session
• REST and Web Services are stateless
• Not linked to an application
• No implicit QEWD Session is appropriate
Copyright © 2016 M/Gateway Developments Ltd
Background on QEWD Sessions
• See Training Course:
– http://docs.qewdjs.com/qewd_training.html
– Part 27 specifically explains how Sessions are
implemented on top of QEWD's embedded
Document Database
– Part 12 for details on Session timeout control
Copyright © 2016 M/Gateway Developments Ltd
No QEWD Session?
• REST and Web Services are stateless
• Not linked to an application
• No implicit QEWD Session is appropriate
• However, you can create and maintain
QEWD sessions in your own handlers
– ewd-session module is available to you
• this.sessions
– You just need to know what to do!
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
• Create a login request handler
– URL will be /api/login
• For security, send as POST HTTP request
• Username & password will be defined and
sent in the Body payload, eg
{"username": "rob", "password": "secret"}
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
routes = [
{
url: '/api/search',
//method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
},
{
url: '/api/login',
method: 'POST',
handler: login
}
]
routes = router.initialise(routes, module.exports);
Add new route into your
REST handler module
myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
finished({token: session.token});
} Add this function into your
REST handler module
myRestService.js
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
finished({token: session.token});
}
Get username and password from POSTed body
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
finished({token: session.token});
}
Validate username and password
- in this example it's just hard-coded for simplicity
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
finished({token: session.token});
}
If valid login credentials, start a session
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
finished({token: session.token});
}
Return the session token
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
It worked, and returned
a QEWD Session Token
Copyright © 2016 M/Gateway Developments Ltd
Use QEWD Monitor to view the Session
Then click on the
Sessions tab
Copyright © 2016 M/Gateway Developments Ltd
Use QEWD Monitor to view the Session
There's our testWebService Session and its token
Copyright © 2016 M/Gateway Developments Ltd
What next?
• Subsequent requests should return the
Session Token, usually in a header
– eg Authorization header
• Your back-end request handlers should
authenticate each request
– Check that the token is valid and not expired
• Your handler can then use the QEWD
Session for temporary data for client's
ongoing dialogue
Copyright © 2016 M/Gateway Developments Ltd
Sounds repetitive!
• It isn't – you can define this logic just once
within the worker process handler module:
– beforeHandler() function
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
module.exports = {
restModule: true,
beforeHandler: function(req, finished) {
//invoked for every received message, before your route-specific handler
// function is invoked
// if this returns false, then your handler function is not invoked
// if you do this, the beforeHandler function must invoke the finished function()
// to return a response and release the worker
// if it returns true or nothing, then your route-specific handler function will be invoked
// in which case, the beforeHander() function must NOT invoke the finished function
},
init: function(application) {
// invoked once when loaded into worker process
}
};
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
module.exports = {
restModule: true,
beforeHandler: function(req, finished) {
// the req argument contains the incoming repackaged request – the same
// data that your handler function received in args.req
},
init: function(application) {
// invoked once when loaded into worker process
}
};
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Authenticate token for all incoming
requests apart from the login one
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
Ignore the login requests
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
Try to get the Authorization header
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
If there's no Authorization header, then
return an error
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
..and tell QEWD not to invoke the
handler for this incoming message
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Separate out the session token from
the Authorization header. The usual
convention is:
Authorization: Bearer {{token}}
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Authenticate the token using QEWD's
Built-in session API
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
If the session was invalid, return
the error reported by the session API
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
Instruct QEWD not to invoke the
normal handler function
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
var auth = req.headers.authorization;
if (!auth) {
finished({error: 'Authorization header missing'});
return false;
}
var token = auth.split('Bearer ')[1];
var status = this.sessions.authenticate(token);
if (status.error) {
finished(status);
return false;
}
req.session = status.session;
}
},
init: function(application) {
If token was valid, save the session
DocumentNode Object into the
incoming request object
QEWD will automatically extract it
and make it available to your handler
function as args.session
Copyright © 2016 M/Gateway Developments Ltd
Using a Session with REST/Web Services
beforeHandler: function(req, finished) {
if (req.path !== '/api/login') {
return this.sessions.authenticateRestRequest(req, finished);
}
},
init: function(application) {
However, you can achieve the same
thing by using this built-in API
I'd recommend you use this in the
beforeHandler() function
Copyright © 2016 M/Gateway Developments Ltd
Here's our routes
init: function(application) {
routes = [
{
url: '/api/search',
//method: 'GET',
handler: search
},
{
url: '/api/patient/:patientId/:heading/summary',
method: 'GET',
handler: getSummary
},
{
url: '/api/save',
method: 'POST',
handler: save
},
{
url: '/api/login',
method: 'POST',
handler: login
}
]
routes = router.initialise(routes, module.exports);
All but /api/login will now
expect a token in the Authorization
header
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
Copyright © 2016 M/Gateway Developments Ltd
Add an invalid Authorization Header
Copyright © 2016 M/Gateway Developments Ltd
Try Logging In
Copyright © 2016 M/Gateway Developments Ltd
Put the token in the Authorization Header & Try Again
Copyright © 2016 M/Gateway Developments Ltd
Put the token in the Authorization Header & Try Again
Now you can invoke the
/api/search (and any other)
API
Copyright © 2016 M/Gateway Developments Ltd
Now let's use the Session
• Make use of it in the /api/search request
– Display it in the response
• First let's put the username into the
Session if the user successfully logs in
Copyright © 2016 M/Gateway Developments Ltd
Add username to Session
function login(args, finished) {
var username = args.req.body.username;
if (!username || username === '') {
return finished({error: 'You must provide a username'});
}
var password = args.req.body.password;
if (!password || password === '') {
return finished({error: 'You must provide a password'});
}
if (username !== 'rob') return finished({error: 'Invalid username'});
if (password !== 'secret') return finished({error: 'Invalid password'});
var session = this.sessions.create('testWebService', 3600);
session.authenticated = true;
session.data.$('username').value = username;
finished({token: session.token});
}
Copyright © 2016 M/Gateway Developments Ltd
Get the username in /api/search
function search(args, finished) {
finished({
test: 'finished ok',
username: args.session.data.$('username').value
});
}
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
• First run POST /api/login
• Copy the returned token and paste it into
the Authorization Header:
– Bearer {{token}}
• Then try GET /api/search
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
Copyright © 2016 M/Gateway Developments Ltd
Restart QEWD and Try it
Now it successfully retrieves and returns
the username from the QEWD Session
Copyright © 2016 M/Gateway Developments Ltd
Session Logout?
• No need for an explicit /logout request
• Session will automatically expire
• If you want, you could set the session as
un-authenticated:
– session.authenticated = false;
Copyright © 2016 M/Gateway Developments Ltd
Session Control
• Remember, once you have authenticated the
token and have the session, you can change the
its timeout and update its expiry, just as in
standard message handlers
– session.timeout = 600;
– session.updateExpiry();
Copyright © 2016 M/Gateway Developments Ltd
REST & Web Services
• You now have everything you need to
support REST and Web Services on your
QEWD system
– In addition to supporting interactive
applications on the same QEWD server, if you
wish
• Add / define as many REST/Web Services
as you want by adding them to the QEWD
startup file
Copyright © 2016 M/Gateway Developments Ltd
Example Files
• You'll find the example files in the QEWD
Github Repo:
• https://github.com/robtweed/qewd/tree/master/example/rest
Copyright © 2016 M/Gateway Developments Ltd
Advanced features
• Although QEWD automates most of what
you need, occasionally you may want to
gain access to Express within the master
process and handle routing etc yourself
– To do this, you add an intercept to the QEWD
startup file, which exposes Express
Copyright © 2016 M/Gateway Developments Ltd
Adding an Intercept to the Startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'cache',
params: {
path: 'c:InterSystemsCache2015-2mgr’
}
}};
var master = require('qewd').master;
var qewd = master.intercept();
master.start(config);
Copyright © 2016 M/Gateway Developments Ltd
Adding an Intercept to the Startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'cache',
params: {
path: 'c:InterSystemsCache2015-2mgr’
}
}};
var master = require('qewd').master;
var qewd = master.intercept();
master.start(config);
now we have access to:
- The qewd object itself: qewd.q
- ewd-qoper8-express: qewd.qx
- Express middleware: qewd.app
Copyright © 2016 M/Gateway Developments Ltd
Adding an Intercept to the Startup file
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'cache',
params: {
path: 'c:InterSystemsCache2015-2mgr’
}
}};
var master = require('qewd').master;
var qewd = master.intercept();
master.start(config);
eg: use some custom Express middleware
qewd.app.use(….);

More Related Content

Viewers also liked

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
Rob 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 Nodes
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 Mode
Rob Tweed
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript Objects
Rob 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 Services
Rob 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 Appliance
Rob Tweed
 
EWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode Object
Rob 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 APIs
Rob 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 4
Rob 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 3
Rob Tweed
 
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
Rob 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 Application
Rob 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 Pattern
Rob Tweed
 
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsEWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
Rob 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 2
Rob Tweed
 
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
Rob 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 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
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 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
Rob Tweed
 

Viewers also liked (20)

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 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
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript Objects
 
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 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 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode Object
 
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 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 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
 
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 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 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsEWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
 
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 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
 
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 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 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 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
 

Similar to EWD 3 Training Course Part 31: Using QEWD for Web and REST Services

EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService FunctionalityEWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
Rob Tweed
 
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsEWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
Rob Tweed
 
EWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDEWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWD
Rob Tweed
 
qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tier
Rob 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 Application
Rob Tweed
 
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
Rob Tweed
 
23003468463PPT.pptx
23003468463PPT.pptx23003468463PPT.pptx
23003468463PPT.pptx
annalakshmi35
 
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESSMERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
annalakshmi35
 
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
Rob Tweed
 
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-418CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
sivakumarmcs
 
sveltekit-en.pdf
sveltekit-en.pdfsveltekit-en.pdf
sveltekit-en.pdf
ssuser65180a
 
Laravel development (Laravel History, Environment Setup & Laravel Installatio...
Laravel development (Laravel History, Environment Setup & Laravel Installatio...Laravel development (Laravel History, Environment Setup & Laravel Installatio...
Laravel development (Laravel History, Environment Setup & Laravel Installatio...
Dilouar Hossain
 
App development with quasar (pdf)
App development with quasar (pdf)App development with quasar (pdf)
App development with quasar (pdf)
wonyong hwang
 
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
Rob Tweed
 
Single Page Web Applications with WordPress REST API
Single Page Web Applications with WordPress REST APISingle Page Web Applications with WordPress REST API
Single Page Web Applications with WordPress REST API
Tejaswini Deshpande
 
EWD 3 Training Course Part 14: Using Ajax for QEWD Messages
EWD 3 Training Course Part 14: Using Ajax for QEWD MessagesEWD 3 Training Course Part 14: Using Ajax for QEWD Messages
EWD 3 Training Course Part 14: Using Ajax for QEWD Messages
Rob Tweed
 
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
Jason452803
 
Introduction to node js - From "hello world" to deploying on azure
Introduction to node js - From "hello world" to deploying on azureIntroduction to node js - From "hello world" to deploying on azure
Introduction to node js - From "hello world" to deploying on azure
Colin Mackay
 
WordPress Acceptance Testing, Solved!
WordPress Acceptance Testing, Solved!WordPress Acceptance Testing, Solved!
WordPress Acceptance Testing, Solved!
Taylor Lovett
 
JavaEE and RESTful development - WSO2 Colombo Meetup
JavaEE and RESTful development - WSO2 Colombo Meetup JavaEE and RESTful development - WSO2 Colombo Meetup
JavaEE and RESTful development - WSO2 Colombo Meetup
Sagara Gunathunga
 

Similar to EWD 3 Training Course Part 31: Using QEWD for Web and REST Services (20)

EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService FunctionalityEWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
 
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsEWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
 
EWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDEWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWD
 
qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tier
 
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-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
 
23003468463PPT.pptx
23003468463PPT.pptx23003468463PPT.pptx
23003468463PPT.pptx
 
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESSMERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
MERN SRTACK WEB DEVELOPMENT NODE JS EXPRESS
 
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
 
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-418CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
18CSC311J WEB DESIGN AND DEVELOPMENT UNIT-4
 
sveltekit-en.pdf
sveltekit-en.pdfsveltekit-en.pdf
sveltekit-en.pdf
 
Laravel development (Laravel History, Environment Setup & Laravel Installatio...
Laravel development (Laravel History, Environment Setup & Laravel Installatio...Laravel development (Laravel History, Environment Setup & Laravel Installatio...
Laravel development (Laravel History, Environment Setup & Laravel Installatio...
 
App development with quasar (pdf)
App development with quasar (pdf)App development with quasar (pdf)
App development with quasar (pdf)
 
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
 
Single Page Web Applications with WordPress REST API
Single Page Web Applications with WordPress REST APISingle Page Web Applications with WordPress REST API
Single Page Web Applications with WordPress REST API
 
EWD 3 Training Course Part 14: Using Ajax for QEWD Messages
EWD 3 Training Course Part 14: Using Ajax for QEWD MessagesEWD 3 Training Course Part 14: Using Ajax for QEWD Messages
EWD 3 Training Course Part 14: Using Ajax for QEWD Messages
 
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
 
Introduction to node js - From "hello world" to deploying on azure
Introduction to node js - From "hello world" to deploying on azureIntroduction to node js - From "hello world" to deploying on azure
Introduction to node js - From "hello world" to deploying on azure
 
WordPress Acceptance Testing, Solved!
WordPress Acceptance Testing, Solved!WordPress Acceptance Testing, Solved!
WordPress Acceptance Testing, Solved!
 
JavaEE and RESTful development - WSO2 Colombo Meetup
JavaEE and RESTful development - WSO2 Colombo Meetup JavaEE and RESTful development - WSO2 Colombo Meetup
JavaEE and RESTful development - WSO2 Colombo Meetup
 

More from Rob Tweed

QEWD Update
QEWD UpdateQEWD Update
QEWD Update
Rob Tweed
 
Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language Feature
Rob 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 Too
Rob Tweed
 
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesEWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
Rob Tweed
 
QEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServicesQEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServices
Rob 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 Too
Rob 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 Locking
Rob 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 CORS
Rob 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/HTTPS
Rob 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 Objects
Rob Tweed
 

More from Rob Tweed (10)

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 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesEWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
 
QEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServicesQEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServices
 
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 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 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 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

Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
kiara pandey
 
Authentication Review-June -2024 AP & TS.pptx
Authentication Review-June -2024 AP & TS.pptxAuthentication Review-June -2024 AP & TS.pptx
Authentication Review-June -2024 AP & TS.pptx
DEMONDUOS
 
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
902basic
 
Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …
908dutch
 
welcome to presentation on Google Apps
welcome to   presentation on Google Appswelcome to   presentation on Google Apps
welcome to presentation on Google Apps
AsifKarimJim
 
Predicting Test Results without Execution (FSE 2024)
Predicting Test Results without Execution (FSE 2024)Predicting Test Results without Execution (FSE 2024)
Predicting Test Results without Execution (FSE 2024)
andrehoraa
 
Mobile App Development Company in Noida - Drona Infotech.
Mobile App Development Company in Noida - Drona Infotech.Mobile App Development Company in Noida - Drona Infotech.
Mobile App Development Company in Noida - Drona Infotech.
Mobile App Development Company in Noida - Drona Infotech
 
InflectraCON 360: Risk-Based Testing for Mission Critical Systems
InflectraCON 360: Risk-Based Testing for Mission Critical SystemsInflectraCON 360: Risk-Based Testing for Mission Critical Systems
InflectraCON 360: Risk-Based Testing for Mission Critical Systems
Inflectra
 
Top Chinese Government-backed APT Groups
Top Chinese Government-backed APT GroupsTop Chinese Government-backed APT Groups
Top Chinese Government-backed APT Groups
SOCRadar
 
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdfAI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
Daniel Zivkovic
 
React Native vs Flutter - SSTech System
React Native vs Flutter  - SSTech SystemReact Native vs Flutter  - SSTech System
React Native vs Flutter - SSTech System
SSTech System
 
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
revolutionary575
 
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
rachitkumar09887
 
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDSAmadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
aadhiyaeliza
 
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
ashiklo9823
 
bangalore Girls call 👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
bangalore Girls call  👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Deliverybangalore Girls call  👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
bangalore Girls call 👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
sunilverma7884
 
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
norina2645
 
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
3610stuck
 
NYGGS 360: A Complete ERP for Construction Innovation
NYGGS 360: A Complete ERP for Construction InnovationNYGGS 360: A Complete ERP for Construction Innovation
NYGGS 360: A Complete ERP for Construction Innovation
NYGGS Construction ERP Software
 
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in CityGirls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
neshakor5152
 

Recently uploaded (20)

Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
Celebrity Girls Call Mumbai 9930687706 Unlimited Short Providing Girls Servic...
 
Authentication Review-June -2024 AP & TS.pptx
Authentication Review-June -2024 AP & TS.pptxAuthentication Review-June -2024 AP & TS.pptx
Authentication Review-June -2024 AP & TS.pptx
 
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
Private Girls Call Navi Mumbai 🛵🚡9820252231 💃 Choose Best And Top Girl Servic...
 
Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …Prada Group Reports Strong Growth in First Quarter …
Prada Group Reports Strong Growth in First Quarter …
 
welcome to presentation on Google Apps
welcome to   presentation on Google Appswelcome to   presentation on Google Apps
welcome to presentation on Google Apps
 
Predicting Test Results without Execution (FSE 2024)
Predicting Test Results without Execution (FSE 2024)Predicting Test Results without Execution (FSE 2024)
Predicting Test Results without Execution (FSE 2024)
 
Mobile App Development Company in Noida - Drona Infotech.
Mobile App Development Company in Noida - Drona Infotech.Mobile App Development Company in Noida - Drona Infotech.
Mobile App Development Company in Noida - Drona Infotech.
 
InflectraCON 360: Risk-Based Testing for Mission Critical Systems
InflectraCON 360: Risk-Based Testing for Mission Critical SystemsInflectraCON 360: Risk-Based Testing for Mission Critical Systems
InflectraCON 360: Risk-Based Testing for Mission Critical Systems
 
Top Chinese Government-backed APT Groups
Top Chinese Government-backed APT GroupsTop Chinese Government-backed APT Groups
Top Chinese Government-backed APT Groups
 
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdfAI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
AI - Your Startup Sidekick (Leveraging AI to Bootstrap a Lean Startup).pdf
 
React Native vs Flutter - SSTech System
React Native vs Flutter  - SSTech SystemReact Native vs Flutter  - SSTech System
React Native vs Flutter - SSTech System
 
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
Busty Girls Call Mumbai 9930245274 Unlimited Short Providing Girls Service Av...
 
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
Agra Girls Call Agra 0X0000000X Unlimited Short Providing Girls Service Avail...
 
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDSAmadeus Travel API, Amadeus Booking API, Amadeus GDS
Amadeus Travel API, Amadeus Booking API, Amadeus GDS
 
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
Vip Girls Call ServiCe Hyderabad 0000000000 Pooja Best High Class Hyderabad A...
 
bangalore Girls call 👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
bangalore Girls call  👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Deliverybangalore Girls call  👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
bangalore Girls call 👀 XXXXXXXXXXX 👀 Rs.9.5 K Cash Payment With Room Delivery
 
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
Celebrity Girls Call Mumbai 🛵🚡9910780858 💃 Choose Best And Top Girl Service A...
 
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
Mumbai Girls Call Mumbai 🎈🔥9930687706 🔥💋🎈 Provide Best And Top Girl Service A...
 
NYGGS 360: A Complete ERP for Construction Innovation
NYGGS 360: A Complete ERP for Construction InnovationNYGGS 360: A Complete ERP for Construction Innovation
NYGGS 360: A Complete ERP for Construction Innovation
 
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in CityGirls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
Girls Call Mysore 000XX00000 Provide Best And Top Girl Service And No1 in City
 

EWD 3 Training Course Part 31: Using QEWD for Web and REST Services

  • 1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 31 Using QEWD for Web and REST Services Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2. Copyright © 2016 M/Gateway Developments Ltd Using QEWD for Web & REST Services • This part of the course assumes that you've taken, at least: • Part 2: Overview of EWD 3 – https://www.slideshare.net/robtweed/ewd-3-overview • Part 4: Installing & Configuring QEWD – https://www.slideshare.net/robtweed/installing-configuring-ewdxpress
  • 3. Copyright © 2016 M/Gateway Developments Ltd QEWD for Web & REST Services • Provides a ready-made platform for developing high-performance, scalable, secure Web and REST Service APIs
  • 4. Copyright © 2016 M/Gateway Developments Ltd QEWD for Web & REST Services • Just as for interactive browser-based applications, the master QEWD process handles concurrency and HTTP(S) access • Your APIs run in the isolated environment of a QEWD worker process – A QEWD worker process invokes a single instance of an API function • So it can safely use synchronous APIs
  • 5. Copyright © 2016 M/Gateway Developments Ltd QEWD for Web & REST Services • An instance of QEWD can simultaneously support: – Multiple interactive browser-based applications; and/or – Multiple REST / Web Service APIs
  • 6. Copyright © 2016 M/Gateway Developments Ltd Typical QEWD Startup file for browser apps var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'cache', params: { path: 'c:InterSystemsCache2015-2mgr’ } }}; var qewd = require('qewd').master; qewd.start(config); If using InterSystems Cache
  • 7. Copyright © 2016 M/Gateway Developments Ltd Typical QEWD Startup file for browser apps var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }; var qewd = require('qewd').master; qewd.start(config); If using GT.M
  • 8. Copyright © 2016 M/Gateway Developments Ltd Defining Routes • To support Web/REST APIs, you must add routes to the startup configuration: – Associate a URL prefix with a back-end QEWD module – QEWD uses this information and automatically looks after the Express middleware and routing needed for incoming matching URLs to invoke the associated back-end module in a worker process
  • 9. Copyright © 2016 M/Gateway Developments Ltd Defining Routes • The URL routes you want to handle are defined in your QEWD startup file: – An array of routing objects
  • 10. Copyright © 2016 M/Gateway Developments Ltd Defining Routes • The URL routes you want to handle are defined in your QEWD startup file: – An array of routing objects – Each routing object defines: • path: the URL path prefix • module: the associated back-end module name/path
  • 11. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes);
  • 12. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes); All incoming HTTP requests with a URL prefix of /api will be handled by a module named myRestService
  • 13. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes); Pass the routes array into the QEWD start() function as its 2nd argument
  • 14. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ {path: '/api', module: 'myRestService'}, {path: '/testing', module: '/path/to/testService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); You can define as many routes as you wish
  • 15. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ {path: '/api', module: 'myRestService'}, {path: '/testing', module: '/path/to/testService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); You can specify a module path if necessary
  • 16. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes); With this route definition, all incoming HTTP requests with a URL prefix of /api will be handled by a module named myRestService
  • 17. Copyright © 2016 M/Gateway Developments Ltd Add Routes to Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes); Any incoming HTTP requests with a URL prefix that doesn't start /api will be rejected by QEWD
  • 18. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route
  • 19. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will reject any incoming HTTP(S) requests whose URL can't be matched in the routes array.
  • 20. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route • Matching requests are repackaged into a JSON message and placed on queue
  • 21. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route • Matching requests are repackaged into a JSON message and placed on queue • Queued message is dispatched to a worker process
  • 22. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route • Matching requests are repackaged into a JSON message and placed on queue • Queued message is dispatched to a worker process • Message is handled in the worker process by the module specified in the route definition
  • 23. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route – Note: matching of routes in the master process is by exact match of URL prefixes defined in startup file route definitions – URL templates are not used in the startup file route definitions
  • 24. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • QEWD Master Process will detect any incoming HTTP(S) requests whose URL starts with a specified route – Startup file route definitions are simply a way to direct a set of incoming requests to a worker process where they are processed by a particular handler module
  • 25. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • In your Worker Process handler module, you specify all the next-level routes you're interested in, and the function to handle each route • Worker Process routes can be templated, eg – /api/customer/:id
  • 26. Copyright © 2016 M/Gateway Developments Ltd How QEWD Routes Work • For example, if the QEWD startup file defines a route for: – /api • The worker process module specified for this route will handle or reject all lower-level URL paths, eg: – /api/search – /api/patient/allergy/detail – /api/user – /api/customer/123456
  • 27. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure module.exports = { restModule: true };
  • 28. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure module.exports = { restModule: true, }; You must tell QEWD that this Module is for handling Web/REST service APIs
  • 29. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure module.exports = { restModule: true, init: function() { // invoked just once when module is first loaded into worker process } };
  • 30. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure module.exports = { restModule: true, init: function() { // invoked just once when module is first loaded into worker process // this is where you define all the lower-level routes you want to handle // - path template // - HTTP method (eg POST, GET, etc) // - handler function to process all requests matching this path } };
  • 31. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { // invoked just once when module is first loaded into worker process } };
  • 32. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] } }; Define an array of routes
  • 33. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] } }; Each route specifies: -url template -method (if not specified, all methods apply) -handler function
  • 34. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; function search(args, finished) { finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/test', method: 'GET', handler: search } ] } }; Define handler function
  • 35. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; function search(args, finished) { finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] } }; Define handler function Arguments: -args: object containing request data -finished: function you use to return response object and release worker process
  • 36. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; function search(args, finished) { finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] } }; Define handler function. It: •should process the incoming message object •must create a response object •must end by invoking finished() function, which: - returns response object back to client - releases worker process to available pool
  • 37. Copyright © 2016 M/Gateway Developments Ltd Handler Module Structure var router = require('qewd-router'); var routes; function search(args, finished) { finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] routes = router.initialise(routes, module.exports); } }; Finally, activate the routes
  • 38. Copyright © 2016 M/Gateway Developments Ltd Handler Function Structure var router = require('qewd-router'); var routes; function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] routes = router.initialise(routes, module.exports); } }; Simple Example:
  • 39. Copyright © 2016 M/Gateway Developments Ltd Let's try it out…
  • 40. Copyright © 2016 M/Gateway Developments Ltd Create a new QEWD startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ {path: '/api', module: 'myRestService'} ]; var qewd = require('qewd').master; qewd.start(config, routes); ~/qewd/rest.js
  • 41. Copyright © 2016 M/Gateway Developments Ltd Create the handler module var router = require('qewd-router'); var routes; function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] routes = router.initialise(routes, module.exports); } }; ~/qewd/node_modules/myRestService.js
  • 42. Copyright © 2016 M/Gateway Developments Ltd Start QEWD using your startup file cd ~/qewd node rest
  • 43. Copyright © 2016 M/Gateway Developments Ltd Try it It works!
  • 44. Copyright © 2016 M/Gateway Developments Ltd Check in the QEWD Node log: *** search args: { "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } Our search() function displayed the entire args object to the console log:
  • 45. Copyright © 2016 M/Gateway Developments Ltd Check in the QEWD Node log: *** search args: { "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({test: 'finished ok'}); } Our search() function displayed the entire messageObj object to the console log:
  • 46. Copyright © 2016 M/Gateway Developments Ltd Your API handler function • So what your QEWD Web/REST Service API handler functions must do is process the incoming message/request object: – To determine what to do – And use appropriate information in the incoming request object to: • Fetch data • Save data • Change data • …etc
  • 47. Copyright © 2016 M/Gateway Developments Ltd Your API handler function • It's therefore vital to understand what's in the args object that's made available by QEWD's router to your handler functions. • Let's take a detailed look…
  • 48. Copyright © 2016 M/Gateway Developments Ltd Check in the QEWD Node log: *** search args: { "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } All the elements of the incoming HTTP request have been broken out and made available in the incoming args Object That was done automatically for us by the QEWD router
  • 49. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } Request Type ie 2nd -level URL path: /api/search - args.req.params.type
  • 50. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The full URL path •args.req.path
  • 51. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The HTTP method used to submit the Request - args.req.method
  • 52. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chro..", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The HTTP headers sent with the request - args.req.headers
  • 53. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The HTTP URL query string (if any) This is where you'll pick up any name/value pairs that were added to the URL
  • 54. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search?name=smith", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": { name: 'smith' }, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The HTTP URL query string (if any) This is where you'll pick up any name/value pairs that were added to the URL, eg /api/search?name=smith - args.req.query.name
  • 55. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search?name=smith", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": { name: 'smith' }, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } Note args.req.path includes the query string too /api/search?name=smith - args.req.query.name
  • 56. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The HTTP request body payload (if any). If the HTTP request was sent as an application/json type, body will contain the pre-parsed JSON Only applies for POST / PUT methods - args.req.body
  • 57. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } The IP address of the client endpoint that sent the request - args.req.ip
  • 58. Copyright © 2016 M/Gateway Developments Ltd Use a proper HTTP Client • A browser will only allow us to test GET requests – Need to test POST and other requests too (eg DELETE, PUT) • Numerous web service/REST clients available – Postman – Chrome's Advanced REST Client (ARC)
  • 59. Copyright © 2016 M/Gateway Developments Ltd Try a POST request
  • 60. Copyright © 2016 M/Gateway Developments Ltd Why did it error? var router = require('qewd-router'); var routes; function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search } ] routes = router.initialise(routes, module.exports); } }; ~/qewd/node_modules/myRestService.js We're specifically only handling the GET method
  • 61. Copyright © 2016 M/Gateway Developments Ltd To handle all methods: var router = require('qewd-router'); var routes; function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({test: 'finished ok'}); } module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', handler: search } ] routes = router.initialise(routes, module.exports); } }; ~/qewd/node_modules/myRestService.js Don't specify method at all
  • 62. Copyright © 2016 M/Gateway Developments Ltd Try Again • Stop and restart QEWD – To make sure the new version of the handler module is loaded into the worker process that handles the request • Alternatively, use the qewd-monitor application and stop all worker processes
  • 63. Copyright © 2016 M/Gateway Developments Ltd Try the POST request again
  • 64. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "POST", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} }
  • 65. Copyright © 2016 M/Gateway Developments Ltd Try a POST with JSON payload
  • 66. Copyright © 2016 M/Gateway Developments Ltd QEWD args Object{ "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "POST", "headers": { "host": "192.168.1.117:8080", "connection": "keep-alive", "cache-control": "max-age=0", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.8", "cookie": "io=5rWbmzRiCtkbJraIAAAA", "if-none-match": "W/"16-QmE+56DO76SiVG9thB82yN42w7U"" }, "params": { "type": "search" }, "query": {}, "body": { "name": "rob" }, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search" }, "session": {} } Here's the body payload - args.req.body.name
  • 67. Copyright © 2016 M/Gateway Developments Ltd Using Path Templates • The routes that you specify in your worker process handler module can include templates – Variable pieces of the URL path are prefixed with a colon • /api/search/:text • This would match, eg: – /api/search/hello – /api/search/world
  • 68. Copyright © 2016 M/Gateway Developments Ltd Using Path Templates • Variable pieces of the URL path are prefixed with a colon • /api/search/:text • The variable name (eg text) is made available in the args object – eg: • /api/search/hello args.text = 'hello' • /api/search/world args.text = 'world'
  • 69. Copyright © 2016 M/Gateway Developments Ltd Using Path Templates • You can have as many variables as you wish in a URL path template, eg • /api/patient/:patientId/:heading/detail/:sourceId • In args we'd find: – args.patientId – args.heading – args.sourceId
  • 70. Copyright © 2016 M/Gateway Developments Ltd Example: add new route to: module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary } ] routes = router.initialise(routes, module.exports); } }; ~/qewd/node_modules/myRestService.js
  • 71. Copyright © 2016 M/Gateway Developments Ltd Add the getSummary function to var headings = { allergies: true, medications: true }; function getSummary(args, finished) { if (headings[args.heading]) { finished({ patient: args.patientId, heading: args.heading, summary: 'details would be here' }); } else { finished({error: 'Invalid heading ' + args.heading}); } } ~/qewd/node_modules/myRestService.js
  • 72. Copyright © 2016 M/Gateway Developments Ltd Try Again • Stop and restart QEWD – To make sure the new version of the handler module is loaded into the worker process that handles the request • Alternatively, use the qewd-monitor application and stop all worker processes
  • 73. Copyright © 2016 M/Gateway Developments Ltd We'll try the new route • With this URL: – GET /api/patient/123456/allergies/summary
  • 74. Copyright © 2016 M/Gateway Developments Ltd Try the new route
  • 75. Copyright © 2016 M/Gateway Developments Ltd Try an invalid heading value
  • 76. Copyright © 2016 M/Gateway Developments Ltd Writing a QEWD Web/REST API • In your worker process handler module, simply define all the URL routes you want to allow, and write a corresponding handler function for each one • What the handler functions actually do is up to you • All the information your functions require will be in the args object • Each of your functions must create a response object and return it using finished() function
  • 77. Copyright © 2016 M/Gateway Developments Ltd Handling Errors • Return a response object that contains an error property – Its value should be the error string you want returned • {error: 'My error message'} – By default, the HTTP status for an error will be 400
  • 78. Copyright © 2016 M/Gateway Developments Ltd Remember our last error:
  • 79. Copyright © 2016 M/Gateway Developments Ltd Controlling the Error status function getSummary(args, finished) { if (headings[args.heading]) { finished({ patient: args.patientId, heading: args.heading, summary: 'details would be here' }); } else { finished({ error: 'Invalid heading ' + args.heading, status: { code: 401 } }); } } ~/qewd/node_modules/myRestService.js Define a custom status code value in the error response
  • 80. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and try again:
  • 81. Copyright © 2016 M/Gateway Developments Ltd Other Errors • Invalid routes – ie ones that aren't defined in the routes array
  • 82. Copyright © 2016 M/Gateway Developments Ltd Specify the error response: module.exports = { restModule: true, init: function() { routes = [ { url: '/api/search', method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary } ] routes = router.initialise(routes, module.exports); router.setErrorResponse(404, 'Not Found'); } }; ~/qewd/node_modules/myRestService.js
  • 83. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD. Try an invalid path:
  • 84. Copyright © 2016 M/Gateway Developments Ltd Why doesn't this work?
  • 85. Copyright © 2016 M/Gateway Developments Ltd It's due to QEWD's handler logic • 2nd -level URL paths are defined behind the scenes with a handler function – So in our example there's one for /api/search – And one for /api/patient – router.setErrorResponse() handles "not found" errors within those paths – But /api/xxxxxx isn't defined as a route so router.setErrorResponse() can't handle it as an error
  • 86. Copyright © 2016 M/Gateway Developments Ltd Requires an additional step • this.setCustomErrorResponse()
  • 87. Copyright © 2016 M/Gateway Developments Ltd Specify the error response: module.exports = { restModule: true, init: function(application) { routes = [ { url: '/api/search', method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary } ] routes = router.initialise(routes, module.exports); router.setErrorResponse(404, 'Not Found'); } }; ~/qewd/node_modules/myRestService.js First, add application as an argument to the init() function
  • 88. Copyright © 2016 M/Gateway Developments Ltd Specify the error response: ] routes = router.initialise(routes, module.exports); router.setErrorResponse(404, 'Not Found'); this.setCustomErrorResponse.call(this, { application: application, errorType: 'noTypeHandler', text: 'Resource Not Found', statusCode: '404' }); } }; ~/qewd/node_modules/myRestService.js Then add the setCustomErrorResponse() Function within the init() function, exactly as shown here
  • 89. Copyright © 2016 M/Gateway Developments Ltd Now it should work
  • 90. Copyright © 2016 M/Gateway Developments Ltd Writing a QEWD Web/REST API • Within your handler functions, you have access to the embedded database via – this.db • eg for this.db.function() calls to legacy code – this.documentStore • This is what you'll usually use for database access – Also now abstracted via this.db.use()
  • 91. Copyright © 2016 M/Gateway Developments Ltd Background on QEWD's Embedded Database • See Training Course: – http://docs.qewdjs.com/qewd_training.html – Parts 17 – 26 for details on the embedded Document Store used by QEWD
  • 92. Copyright © 2016 M/Gateway Developments Ltd Example: /api/save handler routes = [ { url: '/api/search', //method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary }, { url: '/api/save', method: 'POST', handler: save } ] routes = router.initialise(routes, module.exports);
  • 93. Copyright © 2016 M/Gateway Developments Ltd Example: /api/save handler function save(args, finished) { var doc = new this.documentStore.DocumentNode('myData'); var ix = doc.increment(); doc.$(ix).setDocument(args.req.body); finished({saved: ix}); }
  • 94. Copyright © 2016 M/Gateway Developments Ltd Example: /api/save handler function save(args, finished) { var doc = this.db.use('myData'); var ix = doc.increment(); doc.$(ix).setDocument(args.req.body); finished({saved: ix}); } Can now also use this syntax
  • 95. Copyright © 2016 M/Gateway Developments Ltd Example: /api/save handler function save(args, finished) { var doc = this.db.use('myData'); var ix = doc.increment(); doc.$(ix).setDocument(args.req.body); finished({saved: ix}); } This will save the POSTed body JSON into a persistent document named myData
  • 96. Copyright © 2016 M/Gateway Developments Ltd Example: /api/save handler function save(args, finished) { var doc = this.db.use('myData'); var ix = doc.increment(); doc.$(ix).setDocument(args.req.body); finished({saved: ix}); } Each time you invoke it, it will increment the database index
  • 97. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and try it
  • 98. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor • qewd-monitor is a WebSocket-based browser application that is installed automatically if you use one of the QEWD installer scripts – See: – https://www.slideshare.net/robtweed/installing-configuring-ewdxpress – https://github.com/robtweed/qewd/tree/master/installers
  • 99. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor • start qewd-monitor in a browser using the URL – http://192.168.1.119:8080/qewd-monitor/index.html • Change the IP address / port to match your QEWD configuration
  • 100. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor • Log in to qewd-monitor with the managementPassword you specified in your QEWD startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes);
  • 101. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor • Log in to qewd-monitor with the managementPassword you specified in your QEWD startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' } }; var routes = [ { path: '/api', module: 'myRestService' } ]; var qewd = require('qewd').master; qewd.start(config, routes); It's a good idea to change this password from the default!
  • 102. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor Then click on the Document Store tab
  • 103. Copyright © 2016 M/Gateway Developments Ltd Check the database using qewd-monitor And sure enough, here's our POSTed data stored under index 1
  • 104. Copyright © 2016 M/Gateway Developments Ltd QEWD Multi-tasking • Note that your instance of QEWD.js is not only supporting your REST services • It's also simultaneously supporting a WebSocket-based browser application
  • 105. Copyright © 2016 M/Gateway Developments Ltd QEWD Multi-tasking • Note that your instance of QEWD.js is not only supporting your REST services • It's also simultaneously supporting a WebSocket-based browser application • A single instance of QEWD can simultaneously support as many browser applications and REST services as you wish
  • 106. Copyright © 2016 M/Gateway Developments Ltd No QEWD Session? • If you're familiar with QEWD browser- based applications, you'll know that they automatically have a QEWD Session • REST and Web Services are stateless • Not linked to an application • No implicit QEWD Session is appropriate
  • 107. Copyright © 2016 M/Gateway Developments Ltd Background on QEWD Sessions • See Training Course: – http://docs.qewdjs.com/qewd_training.html – Part 27 specifically explains how Sessions are implemented on top of QEWD's embedded Document Database – Part 12 for details on Session timeout control
  • 108. Copyright © 2016 M/Gateway Developments Ltd No QEWD Session? • REST and Web Services are stateless • Not linked to an application • No implicit QEWD Session is appropriate • However, you can create and maintain QEWD sessions in your own handlers – ewd-session module is available to you • this.sessions – You just need to know what to do!
  • 109. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services • Create a login request handler – URL will be /api/login • For security, send as POST HTTP request • Username & password will be defined and sent in the Body payload, eg {"username": "rob", "password": "secret"}
  • 110. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services routes = [ { url: '/api/search', //method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary }, { url: '/api/login', method: 'POST', handler: login } ] routes = router.initialise(routes, module.exports); Add new route into your REST handler module myRestService.js
  • 111. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; finished({token: session.token}); } Add this function into your REST handler module myRestService.js
  • 112. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; finished({token: session.token}); } Get username and password from POSTed body
  • 113. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; finished({token: session.token}); } Validate username and password - in this example it's just hard-coded for simplicity
  • 114. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; finished({token: session.token}); } If valid login credentials, start a session
  • 115. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; finished({token: session.token}); } Return the session token
  • 116. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it
  • 117. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it It worked, and returned a QEWD Session Token
  • 118. Copyright © 2016 M/Gateway Developments Ltd Use QEWD Monitor to view the Session Then click on the Sessions tab
  • 119. Copyright © 2016 M/Gateway Developments Ltd Use QEWD Monitor to view the Session There's our testWebService Session and its token
  • 120. Copyright © 2016 M/Gateway Developments Ltd What next? • Subsequent requests should return the Session Token, usually in a header – eg Authorization header • Your back-end request handlers should authenticate each request – Check that the token is valid and not expired • Your handler can then use the QEWD Session for temporary data for client's ongoing dialogue
  • 121. Copyright © 2016 M/Gateway Developments Ltd Sounds repetitive! • It isn't – you can define this logic just once within the worker process handler module: – beforeHandler() function
  • 122. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services module.exports = { restModule: true, beforeHandler: function(req, finished) { //invoked for every received message, before your route-specific handler // function is invoked // if this returns false, then your handler function is not invoked // if you do this, the beforeHandler function must invoke the finished function() // to return a response and release the worker // if it returns true or nothing, then your route-specific handler function will be invoked // in which case, the beforeHander() function must NOT invoke the finished function }, init: function(application) { // invoked once when loaded into worker process } };
  • 123. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services module.exports = { restModule: true, beforeHandler: function(req, finished) { // the req argument contains the incoming repackaged request – the same // data that your handler function received in args.req }, init: function(application) { // invoked once when loaded into worker process } };
  • 124. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { Authenticate token for all incoming requests apart from the login one
  • 125. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services Ignore the login requests beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) {
  • 126. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services Try to get the Authorization header beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) {
  • 127. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services If there's no Authorization header, then return an error beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) {
  • 128. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services ..and tell QEWD not to invoke the handler for this incoming message beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) {
  • 129. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { Separate out the session token from the Authorization header. The usual convention is: Authorization: Bearer {{token}}
  • 130. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { Authenticate the token using QEWD's Built-in session API
  • 131. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { If the session was invalid, return the error reported by the session API
  • 132. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { Instruct QEWD not to invoke the normal handler function
  • 133. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { var auth = req.headers.authorization; if (!auth) { finished({error: 'Authorization header missing'}); return false; } var token = auth.split('Bearer ')[1]; var status = this.sessions.authenticate(token); if (status.error) { finished(status); return false; } req.session = status.session; } }, init: function(application) { If token was valid, save the session DocumentNode Object into the incoming request object QEWD will automatically extract it and make it available to your handler function as args.session
  • 134. Copyright © 2016 M/Gateway Developments Ltd Using a Session with REST/Web Services beforeHandler: function(req, finished) { if (req.path !== '/api/login') { return this.sessions.authenticateRestRequest(req, finished); } }, init: function(application) { However, you can achieve the same thing by using this built-in API I'd recommend you use this in the beforeHandler() function
  • 135. Copyright © 2016 M/Gateway Developments Ltd Here's our routes init: function(application) { routes = [ { url: '/api/search', //method: 'GET', handler: search }, { url: '/api/patient/:patientId/:heading/summary', method: 'GET', handler: getSummary }, { url: '/api/save', method: 'POST', handler: save }, { url: '/api/login', method: 'POST', handler: login } ] routes = router.initialise(routes, module.exports); All but /api/login will now expect a token in the Authorization header
  • 136. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it
  • 137. Copyright © 2016 M/Gateway Developments Ltd Add an invalid Authorization Header
  • 138. Copyright © 2016 M/Gateway Developments Ltd Try Logging In
  • 139. Copyright © 2016 M/Gateway Developments Ltd Put the token in the Authorization Header & Try Again
  • 140. Copyright © 2016 M/Gateway Developments Ltd Put the token in the Authorization Header & Try Again Now you can invoke the /api/search (and any other) API
  • 141. Copyright © 2016 M/Gateway Developments Ltd Now let's use the Session • Make use of it in the /api/search request – Display it in the response • First let's put the username into the Session if the user successfully logs in
  • 142. Copyright © 2016 M/Gateway Developments Ltd Add username to Session function login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; session.data.$('username').value = username; finished({token: session.token}); }
  • 143. Copyright © 2016 M/Gateway Developments Ltd Get the username in /api/search function search(args, finished) { finished({ test: 'finished ok', username: args.session.data.$('username').value }); }
  • 144. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it • First run POST /api/login • Copy the returned token and paste it into the Authorization Header: – Bearer {{token}} • Then try GET /api/search
  • 145. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it
  • 146. Copyright © 2016 M/Gateway Developments Ltd Restart QEWD and Try it Now it successfully retrieves and returns the username from the QEWD Session
  • 147. Copyright © 2016 M/Gateway Developments Ltd Session Logout? • No need for an explicit /logout request • Session will automatically expire • If you want, you could set the session as un-authenticated: – session.authenticated = false;
  • 148. Copyright © 2016 M/Gateway Developments Ltd Session Control • Remember, once you have authenticated the token and have the session, you can change the its timeout and update its expiry, just as in standard message handlers – session.timeout = 600; – session.updateExpiry();
  • 149. Copyright © 2016 M/Gateway Developments Ltd REST & Web Services • You now have everything you need to support REST and Web Services on your QEWD system – In addition to supporting interactive applications on the same QEWD server, if you wish • Add / define as many REST/Web Services as you want by adding them to the QEWD startup file
  • 150. Copyright © 2016 M/Gateway Developments Ltd Example Files • You'll find the example files in the QEWD Github Repo: • https://github.com/robtweed/qewd/tree/master/example/rest
  • 151. Copyright © 2016 M/Gateway Developments Ltd Advanced features • Although QEWD automates most of what you need, occasionally you may want to gain access to Express within the master process and handle routing etc yourself – To do this, you add an intercept to the QEWD startup file, which exposes Express
  • 152. Copyright © 2016 M/Gateway Developments Ltd Adding an Intercept to the Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'cache', params: { path: 'c:InterSystemsCache2015-2mgr’ } }}; var master = require('qewd').master; var qewd = master.intercept(); master.start(config);
  • 153. Copyright © 2016 M/Gateway Developments Ltd Adding an Intercept to the Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'cache', params: { path: 'c:InterSystemsCache2015-2mgr’ } }}; var master = require('qewd').master; var qewd = master.intercept(); master.start(config); now we have access to: - The qewd object itself: qewd.q - ewd-qoper8-express: qewd.qx - Express middleware: qewd.app
  • 154. Copyright © 2016 M/Gateway Developments Ltd Adding an Intercept to the Startup file var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'cache', params: { path: 'c:InterSystemsCache2015-2mgr’ } }}; var master = require('qewd').master; var qewd = master.intercept(); master.start(config); eg: use some custom Express middleware qewd.app.use(….);