UNPKG

44.6 kBMarkdownView Raw
1#Contents
2
3- [QEWD-Up Life Cycle Events](#qewd-up-life-cycle-events)
4- [Life Cycle Points](#life-cycle-points)
5 - [Monolithic QEWD-Up Application](#monolithic-qewd-up-application)
6 - [Available Life-Cycle Hooks in Chronological Sequence](#available-life-cycle-hooks-in-chronological-sequence)
7 - [MicroService-based QEWD-Up Application](#microService-based-qewd-up-application)
8 - [Available MicroService Life-Cycle Hooks in Chronological Sequence](#available-microservice-life-cycle-hooks-in-chronological-sequence)
9- [Life-Cycle Hook Specifications](#life-cycle-hook-specifications)
10 - [addMiddleware](#addmiddleware)
11 - [onStarted](#onstarted)
12 - [onWSRequest](#onwsrequest)
13 - [onWorkerLoad](#onWorkerLoad)
14 - [beforeHandler](#beforehandler)
15 - [onMSResponse](#onmsresponse)
16 - [onOrchResponse](#onorchresponse)
17 - [onWSResponse](#onwsresponse)
18
19# QEWD-Up Life Cycle Events
20
21QEWD-Up provides hooks at various points in the life-cycle of an API, allowing you fine control over the behaviour of your API, whilst allowing you to document your API Life-Cycles in an easy-to-find and easy-to-understand way.
22
23# Life Cycle Points
24
25The points at which you can intercept the life-cycle of an API depends on whether your application is a monolithic one or implemented as a set of MicroServices.
26
27## Monolithic QEWD-Up Application
28
29Understanding how and why to control the life-cycle of your monolithic application APIs requires an understanding of QEWD's Master Process / Worker Process architecture. **Note**: this applies to both Native and Docker versions of QEWD Monolithic applications.
30
31### The QEWD Run-Time Environment
32
33This is described [diagrammatically here](https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices) in slides 4 - 40.
34
35In summary, all incoming requests are received by QEWD's Master Process. In the case of REST API requests, these are received by the embedded Express web server.
36
37The requests are then placed in a queue within the Master Process. By default, any request payloads are assumed to be JSON-formatted, and the request is passed unchanged from Express to QEWD's queue.
38
39When a new item is added to the queue, the Master Process tries to send it to an available Worker Process. By default, QEWD-Up configures QEWD with a pool of 2 Worker Processes, but you can modify this to whatever pool size you require in the *config.json* file.
40
41Your API handler module is invoked within the Worker process that receives the incoming request. Your API handler module has exclusive access to the Worker process, so, unusually for a Node.js environment, your handler module does not need to be concerned about concurrency. Additionally, the Worker process has synchronous access to the integrated persistent JSON database.
42
43Your API handler module can make use of any 3rd party Node.js modules you wish to use, and, subject to network availability, your API handler module can also send requests to external services and resources.
44
45Once your handler logic is completed, you must invoke QEWD's *finished* function which does two key things:
46
47- it tells QEWD that you have finished with the Worker process;
48- it allows you to define the JSON response object that you want to return to the REST client
49
50The *finished* function actually sends the JSON response object to the QEWD Master process, and, on receipt of this message, the Master process returns the Worker process back to the available pool, ready to handle the next queued request.
51
52Finally, the Master process sends the JSON response to the REST Client. By default, no changes are made to the JSON response by the Master process: QEWD assumes that the REST Client is expecting JSON-formatted.
53
54### Available Life-Cycle Hooks in Chronological Sequence
55
56- **addMiddleware**: triggered during QEWD's Master process startup, just after its integrated Web Server (Express by default) is configured. This hook allows you to customise the Web Server middleware, eg to handle XML, set payload size limits etc. It can optionally be used together with the **config.json** file's *qewd.bodyParser* property to customise, replace or override the standard JSON body parsing.
57
58- **onStarted**: triggered after QEWD's Master process starts and before any APIs are allowed to be handled. It gives you access to QEWD's *this* object and can be used for complex situations, for example where you want to integrate QEWD with a third-party module that provides a turnkey server environment. You can often use this kook instead of the **addMiddleware** one, as it also gives you access to the Web Server's *app* object. This hook is invoked in QEWD's Master process.
59
60- **onWSRequest**: triggered as part of your Web Server's Middleware chain, before each incoming API request is routed to its appropriate handler method. Its usual purpose is to allow you to modify some aspect(s) of the incoming request, for example modifying and/or adding HTTP headers, and/or modifying the request payload. This hook is invoked in the QEWD Master process, before the incoming request is queued, and applies to **ALL** incoming requests.
61
62- **beforeHandler**: triggered in the QEWD Worker process that handles the incoming request **before** your handler method is invoked. This hook is applied to **all** incoming requests. Typically it is used to define logic that should be applied to all incoming requests, for example to test user authentication. The *beforeHandler* can be defined to accept or reject requests based on their headers and/or content, and return an appropriate error response for requests that are rejected. Rejected messages will **not** invoke your handler method. Because this hook is invoked in the QEWD Worker process, it has access to the integrated persistent JSON database and/or QEWD Session (if appropriate).
63
64- **onWSResponse**: triggered in the QEWD Master process on receipt of the response from the Worker process and before the JSON response is returned to the REST Client. Typically, this hook is used to modify the response content, eg adding or changing response headers and/or modifying the payload format (eg from JSON to XML). This hook is applied to **all** responses. **Note**: if you implement an *onWSResponse* hook, then you take on responsibility for sending all responses back to the REST Client, including error responses.
65
66
67## MicroService-based QEWD-Up Application
68
69Understanding how and why to control the life-cycle of your MicroService application APIs requires an understanding of:
70
71- **each MicroService instance**: Each MicroService is an instance of QEWD, with its Master Process / Worker Process architecture;
72- **how QEWD's MicroServices interoperate**. In summary, all incoming requests from REST Clients are handled by a MicroService known as the *Orchestrator*. This communicates with all your other MicroService QEWD instances over persistent WebSocket connections. The *Orchestrator* MicroService routes incoming requests to the appropriate MicroService that is designated to handle that request. Responses are returned to the *Orchestrator* which then forwards them to the REST client.
73
74### The QEWD Run-Time Environment
75
76Each MicroService (including the *Orchestrator*) is a complete QEWD instance, described [diagrammatically here](https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices) in slides 4 - 40.
77
78How the MicroServices communicate and interoperate is described [diagrammatically here](https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices) in slides 100 - 115.
79
80In summary, all incoming requests are received by each QEWD instance's Master Process.
81
82In the case of REST API requests sent from a REST Client and received by the Orchestrator, these are received by the embedded web server (Express by default).
83
84In the case of WebSocket messages received by your other MicroService QEWD instances, these are handled by the standard Node.js **socket.io** module which is included and automatically configured within QEWD.
85
86Requests received by the *Orchestrator* are repackaged as JSON messages and immediately routed within its Master Process via the appropriate WebSocket connection to the MicroService designated to handle the request.
87
88Requests received by your other MicroServices are placed in a queue within the Master Process. When a new item is added to this queue, the Master Process tries to send it to an available Worker Process. By default, QEWD-Up configures QEWD with a pool of 2 Worker Processes, but you can modify this to whatever pool size you require in the *config.json* file.
89
90Your API handler module is invoked within the Worker process that receives the incoming request. Your API handler module has exclusive access to the Worker process, so, unusually for a Node.js environment, your handler module does not need to be concerned about concurrency. Additionally, the Worker process has synchronous access to the integrated persistent JSON database.
91
92Your API handler module can make use of any 3rd party Node.js modules you wish to use, and, subject to network availability, your API handler module can also send requests to external services and resources.
93
94Once your handler logic is completed, you must invoke QEWD's *finished* function which does two key things:
95
96- it tells QEWD that you have finished with the Worker process;
97- it allows you to define the JSON response object that you want to return to the REST client
98
99The *finished* function actually sends the JSON response object to the QEWD Master process, and, on receipt of this message, the Master process returns the Worker process back to the available pool, ready to handle the next queued request.
100
101Your MicroService's Master process returns the JSON response to the *Orchestrator* MicroService via the WebSocket connection.
102
103On receipt of the response from another MicroService, the *Orchestrator* returns the response to the REST Client. By default, no changes are made to the JSON response by the Orchestrator
104
105
106### Available MicroService Life-Cycle Hooks in Chronological Sequence
107
108
109#### Startup of Each MicroService
110
111- **addMiddleware**: this is appropriate only to the *Orchestrator*. It is triggered during QEWD's Master process startup, just after its integrated Web Server (Express by default) is configured. This hook allows you to customise the Web Server middleware, eg to handle XML, set payload size limits etc. It can optionally be used together with the **config.json** file's *qewd.bodyParser* property to customise, replace or override the standard JSON body parsing.
112
113- **onStarted**: this is triggered after QEWD's Master process starts and before any APIs are allowed to be handled. It gives you access to QEWD's *this* object and can be used for complex situations, for example where you want to integrate QEWD with a third-party module that provides a turnkey server environment. On the *Orchestrator* you can often use this kook instead of the **addMiddleware** one, as it also gives you access to the Web Server's *app* object. This hook is invoked in QEWD's Master process.
114
115
116#### Request Received by Orchestrator
117
118- **onWSRequest**: triggered as part of your Orchestrator's Middelware chain, and before each incoming API request is routed to its appropriate handling MicroService destination. Its usual purpose is to allow you to modify some aspect(s) of the incoming request, for example modifying and/or adding HTTP headers, and/or modifying the request payload. This hook is invoked in the QEWD Master process, before the incoming request is forwarded to its handling MicroService, and applies to **ALL** incoming requests.
119
120
121#### Request forwarded from Orchestrator Received by Handling MicroService
122
123- **onWorkerLoad**: triggered when a new QEWD Worker process starts and loads the generated Application Module that contains all your application's handler modules. A typical use for this event hook is to load one or more third-party Node.js modules that should be available to all your handler modules.
124
125- **beforeHandler**: triggered in the QEWD Worker process that handles the incoming request **before** your handler method is invoked. This hook is applied to **all** incoming requests. Typically it is used to define logic that should be applied to all incoming requests. A typical use for this event hook in a MicroService is to create and maintain a QEWD Session, linked to the JWT attached to the request. Rejected messages will **not** invoke your handler method. Because this hook is invoked in the QEWD Worker process, it has access to the integrated persistent JSON database.
126
127- **onMSResponse**: applied to a specific API and triggered on the Master process on receipt of the response from the Worker process that handled the API. This allows you to intercept the response from your API handler and optionally invoke one or more additional API requests that may be sent to be handled on other MicroServices. This allows you, for example, to build up a composite response from a series of chained APIs spread across multiple MicroServices
128
129#### Response received by Orchestrator from MicroService
130
131- **onOrchResponse**: applied to a specific API and triggered on the Orchestrator's Master process before the response is returned to the REST Client. This hook allows you to do several things, including:
132
133 - repackaging/reformatting the response
134 - intercepting the flow and sending out one or more new API requests to the MicroServices that will handle them, compiling a composite response that will ultimately be returned to the REST Client
135
136- **onWSResponse**: triggered in the QEWD Master process on receipt of **all** responses from a MicroService and before the JSON response is returned to the REST Client. Typically, this hook is used to globally modify the response content, eg adding or changing response headers and/or modifying the payload format (eg from JSON to XML). **Note**: if you implement an *onWSResponse* hook, then you take on responsibility for sending all responses back to the REST Client, including error responses.
137
138
139# Life-Cycle Hook Specifications
140
141## addMiddleware
142
143### Filename and Directory location
144
145The filename is **addMiddleware.js**. The name is case-sensitive.
146
147Its placement depends on what mode you are using and/or microservice you are specifying it for
148
149#### Monolith
150
151 ~/dockerExample
152 |
153 |_ addMiddleware.js
154 |
155 |_ configuration
156 |
157 |_ apis
158 |
159
160#### MicroService: Orchestrator
161
162 ~/microserviceExample
163 |
164 |_ configuration
165 |
166 |_ orchestrator
167 | |
168 | |_ addMiddleware.js
169
170
171#### MicroService: Other Microservice
172
173*eg* for a MicroService named *login_service*:
174
175 ~/microserviceExample
176 |
177 |_ configuration
178 |
179 |_ login_service
180 | |
181 | |_ addMiddleware.js
182
183
184### Module structure
185
186Your *addModule.js* file should export a function of the structure shown below:
187
188 module.exports = function(bodyParser, app, qewdRouter, config) {
189 // add/ define / configure your WebServer middleware
190 };
191
192### Module Function Arguments
193
194#### bodyParser
195
196This is the bodyParser object that has been loaded and configured for the Web Server. By default, QEWD uses the *body-parser* module and configures it to handle and parse JSON content.
197
198if you want to use the *body-parser* module, but apply different/additional configuration settings to it, then, in your *config.json* file, use the property *qewd.bodyParser* to specify it.
199
200For example, in Monolith mode:
201
202 {
203 "qewd_up": true,
204 "qewd": {
205 "bodyParser": "body-parser"
206 }
207 }
208
209or in the Orchestrator MicroService:
210
211 {
212 "qewd_up": true,
213 "orchestrator": {
214 "qewd": {
215 "bodyParser": "body-parser"
216 }
217 }
218 }
219
220This will stop QEWD from configuring it automatically.
221
222If you want to use a different bodyParser module, then you must first specify it in your *config.json* file, eg:
223
224 {
225 "qewd_up": true,
226 "qewd": {
227 "bodyParser": "body-parse"
228 }
229 }
230
231and then configure it within your *addMiddleware** module.
232
233
234#### app
235
236This is the WebServer (eg Express) object. You can use this to, for example, specify your own custom *app.use* directives.
237
238For example, here is an *addMiddleware* module that configures payload size limits (assuming *qewd.bodyParser* was specified in the *config.json* file):
239
240
241 module.exports = function(bodyParser, app) {
242 app.use(bodyParser.json({limit: '1mb'}));
243 app.use(bodyParser.urlencoded({limit: '1mb', extended: true}));
244 };
245
246#### qewdRouter
247
248This is QEWD's REST API routing function. You can use this to insert a middleware chain prior to QEWD taking over the API routing. For example:
249
250 var cors = require('cors');
251 module.exports = function(bodyParser, app, qewdRouter) {
252 app.use('/api/login', cors(), qewdRouter());
253 };
254
255
256Note that for the above to work, you'd need to include *cors* in your *install_modules.json* file, eg:
257
258 [
259 "cors"
260 ]
261
262QEWD-Up will ensure that this is loaded from NPM before starting QEWD.
263
264#### config
265
266This is the *config* object that contains your QEWD instance's configuration settings. It can be useful for conditional logic, for example that determines the configured database type, eg:
267
268 module.exports = function(bodyParser, app, qewdRouter, config) {
269 if (config.database.type === 'gtm') {
270 // ... etc
271 }
272 };
273
274You can modify the values within the config object, but you should take care doing this. Note that by the time the *addMiddleware* hook is invoked, changing some *config* properties may not have any effect. You should try to set the values you require within the *qewd* property in the relevant part of your *config.json* file.
275
276
277## onStarted
278
279### Filename and Directory location
280
281The filename is **onStarted.js**. Its name is case-sensitive.
282
283Its placement depends on what mode you are using and/or microservice you are specifying it for
284
285#### Monolith
286
287 ~/dockerExample
288 |
289 |_ onStarted.js
290 |
291 |_ configuration
292 |
293 |_ apis
294 |
295
296#### MicroService: Orchestrator
297
298 ~/microserviceExample
299 |
300 |_ configuration
301 |
302 |_ orchestrator
303 | |
304 | |_ onStarted.js
305
306
307#### MicroService: Other Microservice
308
309*eg* for a MicroService named *login_service*:
310
311 ~/microserviceExample
312 |
313 |_ configuration
314 |
315 |_ login_service
316 | |
317 | |_ onStarted.js
318
319
320### Module structure
321
322Your *onStarted.js* file should export a function of the structure shown below:
323
324 module.exports = function(config, app, qewdRouter) {
325 // perform startup tasks
326 };
327
328### Module Function Context
329
330The **this** object within your *onStarted* module is the QEWD Master Process object, so you have access to all its properties and method.
331
332### Module Function Arguments
333
334#### config
335
336This is the *config* object that contains your QEWD instance's configuration settings. It can be useful for conditional logic, for example that determines the configured database type, eg:
337
338 module.exports = function(config) {
339 if (config.database.type === 'gtm') {
340 // ... etc
341 }
342 };
343
344You can modify the values within the config object, but you should take care doing this. Note that by the time the *onStarted* hook is invoked, changing some *config* properties is unlikely to have any effect, as most of the startup actions that depend on them will have already taken place. You should try to set the values you require within the *qewd* property in the relevant part of your *config.json* file.
345
346
347#### app
348
349This is the WebServer (eg Express) object. You can use this to, for example, specify your own custom *app.use* directives.
350
351For example, here is an *addMiddleware* module that configures payload size limits (assuming *qewd.bodyParser* was specified in the *config.json* file):
352
353
354 module.exports = function(config, app) {
355 app.use(bodyParser.json({limit: '1mb'}));
356 app.use(bodyParser.urlencoded({limit: '1mb', extended: true}));
357 };
358
359#### qewdRouter
360
361This is QEWD's REST API routing function. You can use this to insert a middleware chain prior to QEWD taking over the API routing. For example:
362
363 var cors = require('cors');
364 module.exports = function(config, app, qewdRouter) {
365 app.use('/api/login', cors(), qewdRouter());
366 };
367
368
369Note that for the above to work, you'd need to include *cors* in your *install_modules.json* file, eg:
370
371 [
372 "cors"
373 ]
374
375QEWD-Up will ensure that this is loaded from NPM before starting QEWD.
376
377
378## onWSRequest
379
380### Filename and Directory location
381
382The filename is **onWSRequest.js**. Its name is case-sensitive.
383
384The *onWSRequest* hook is only relevant to:
385
386- a QEWD-Up Monolithic application;
387- the Orchestrator MicroService in a QEWD-Up MicroService application
388
389Its placement depends on what mode you are using and/or microservice you are specifying it for
390
391#### Monolith
392
393 ~/dockerExample
394 |
395 |_ onWSrequest.js
396 |
397 |_ configuration
398 |
399 |_ apis
400 |
401
402#### MicroService: Orchestrator
403
404 ~/microserviceExample
405 |
406 |_ configuration
407 |
408 |_ orchestrator
409 | |
410 | |_ onWSRequest.js
411
412
413
414### Module structure
415
416Your *onWSRequest.js* file should export a function of the structure shown below:
417
418 module.exports = function(req, res, next) {
419 // perform onWSRequest processing
420 next();
421 };
422
423### Module Function Arguments
424
425#### req
426
427The WebServer (eg Express by default) request object
428
429#### res
430
431The WebServer (eg Express by default) response object
432
433#### next
434
435The WebServer (eg Express by default) next() function, allowing control to be passed to the next function in the middleware chain.
436
437### Example
438
439This *onWSRequest* example implements CSRF protection for all incoming requests:
440
441 module.exports = function(req, res, next) {
442
443 function sendError(message) {
444 res.set('content-length', message.length);
445 res.status(400).send(message);
446 }
447
448 if (!req.headers) {
449 return sendError('Invalid request: headers missing');
450 }
451 if (!req.headers['x-requested-with']) {
452 return sendError('Invalid request: x-requested-with header missing');
453 }
454 if (req.headers['x-requested-with'] !== 'XMLHttpRequest') {
455 return sendError('Invalid request: x-requested-with header invalid');
456 }
457 next();
458 };
459
460
461
462## onWorkerLoad
463
464
465### Filename and Directory location
466
467The filename is **onWorkerLoad.js**. Its name is case-sensitive.
468
469This event hook is useful if most or all of the handler modules for the MicroService require the use of the same third-party Node.js module. Rather than each handler module *require()*ing the module, you can do this once when the QEWD Worker loads the Application Module that QEWD-Up generates, and augment the *this* object with the loaded Node.js module(s).
470
471A more complex scenario is where you want to load a Node.js module and then instantiate an instance of a class or object from it, and make that class or object available to some or all of your handler modules.
472
473Placement of the *onWorkerLoad.js* file is within a MicroService's folder, eg:
474
475 ~/microserviceExample
476 |
477 |_ configuration
478 |
479 |_ login_service
480 | |
481 | |_ onWorkerLoad.js
482
483
484### Module structure
485
486Your *onWorkerLoad.js* file should export a function of the structure shown below:
487
488 module.exports = function() {
489 // onWorkerLoad logic here
490 };
491
492### Module Function Context
493
494The *onWorkerLoad* module's **this** object is the QEWD Worker Process object. This provides access to, for example:
495
496- **this.db.use**: Giving access to a document within the integrated persistent JSON database
497- **this.session**: The QEWD Session (if relevant)
498- **this.userDefined**: Any custom objects that were defined at QEWD Startup
499
500Typically you'll load one or more third-party modules and add them (or a derived instantiated class or object) to *this*.
501
502
503
504## beforeHandler
505
506
507### Filename and Directory location
508
509The filename is **beforeHandler.js**. Its name is case-sensitive.
510
511It is applied to **all** of the above API requests, and is invoked on the QEWD Worker process that handles the API
512
513Its most common uses are:
514
515 - in a Monolithic application, for determining whether or not the request has been authenticated, and rejecting un-authenticated or otherwise invalid requests before the appropriate API handler method is invoked.
516
517 - in a MicroService application, for establishing and sunsequently maintaining a QEWD Session that is linked to the incoming JWT that is included with the user's request, eg for cacheing user-specific data for the duration of the user's activity.
518
519**Note**: In a Monolithic application, although the *beforeHandler* hook is applied to all of the above API requests, it can be over-ridden on a per-API basis by setting a route's [*applyBeforeHandler* property to *false*](https://github.com/robtweed/qewd/blob/master/up/docs/Routes.md#the-beforehandler-hook-in-monolithic-applications).
520
521The placement of the *beforeHandler.js* file depends on what mode you are using:
522
523#### Monolith
524
525 ~/dockerExample
526 |
527 |_ configuration
528 |
529 |_ apis
530 | |
531 | |_ beforeHandler.js
532 |
533
534#### MicroService (Orchestrator)
535
536 ~/microserviceExample
537 |
538 |_ configuration
539 |
540 |_ orchestrator
541 | |
542 | apis
543 | |
544 | |_ beforeHandler.js
545
546
547#### MicroService (MicroService)
548
549If you have defined your handler modules directly within your MicroService folder, then the *beforeHandler.js* module should also be placed there, eg:
550
551
552 ~/microserviceExample
553 |
554 |_ configuration
555 |
556 |_ login_service
557 | |
558 | |
559 | |_ beforeHandler.js
560
561
562Alternatively, if you have placed all your handlers within their own *apis* folder, you should also place the *beforeHandler.js* module within the *apis* folder, eg:
563
564
565 ~/microserviceExample
566 |
567 |_ configuration
568 |
569 |_ login_service
570 | |
571 | apis
572 | |
573 | |_ beforeHandler.js
574
575
576### Module structure
577
578Your *beforeHandler.js* file should export a function of the structure shown below:
579
580 module.exports = function(req, finished) {
581 // beforeHandler logic here
582 };
583
584### Module Function Context
585
586The *beforeHandler* module's **this** object is the QEWD Worker Process object. This provides access to, for example:
587
588- **this.db.use**: Giving access to a document within the integrated persistent JSON database
589- **this.session**: The QEWD Session (if relevant)
590- **this.userDefined**: Any custom objects that were defined at QEWD Startup
591
592### Module Function Arguments
593
594#### req
595
596This is the version of the request object that is created by QEWD and transmitted to the Worker process. Its most relevant/useful properties are as follows:
597
598- **path**: The API route path, eg */api/info*
599- **method**: The HTTP request method
600- **headers**: The HTTP request headers object. You'll probably be most interested in the *headers.authorization* value which you can use to test
601- **query**: The URL query string (if any), parsed into an object
602- **body**: For POST/PUT requests, the request body payload (if any), parsed into an object
603- **ip**: The IP Address of the location that was determined to have sent the request (which may be a router or gateway IP address)
604
605#### finished
606
607The QEWD Worker function that you should use if you want to reject the request and return an error message, eg:
608
609 module.exports = function(req, finished) {
610 if (!req.headers.authorization) {
611 finished({error: 'Missing Authorization Header'});
612 return false; // stops QEWD invoking your request's normal handler method
613 }
614 //... etc
615 };
616
617The *finished* function performs two tasks in QEWD:
618
619- it defines the object to be returned to the REST Client
620- it tells QEWD's Master process that you have finished with the Worker process, and it can be returned to the available pool
621
622**Note:** If you invoke the *finished()* function, you **MUST** *return false* from your *beforeHandler* module. Doing so instructs QEWD to bypass your normal handler method. See the example above
623
624### Example
625
626In a MicroService application, you can establish and maintain a QEWD Session that is linked to the incoming request's JWT simply by defining the following *beforeHandler* module:
627
628 module.exports = function(req, finished) {
629 req.qewdSession = this.qewdSessionByJWT.call(this, req);
630 return true;
631 };
632
633Your handler modules can then access this QEWD Session via *args.req.qewdSession*.
634
635
636## onMSResponse
637
638
639### Filename and Directory location
640
641The filename is **onMSResponse.js**. Its name is case-sensitive.
642
643The *onMSResponse* hook is only relevant to MicroService applications
644
645It is applied to the specified API, and is invoked on the QEWD Master process of the MicroService that handled the API, on receipt of the handler response from the QEWD Worker process and before the MicroService returns its response to the Orchestrator (or other originating MicroService).
646
647The *onMSResponse* hook allows you to intercept the response from your API's handler, and send one or more further API requests, eg to other MicroServices, in order to build up a complex, composite response.
648
649Because the *onMSResponse* runs on the MicroService that handles the API, if you send out other API requests, those APIs must be available for use on your handling MicroService. This is specified in the route definitions in the *routes.json* file, via the *from_microservices* property. For example, if we wanted to use the */api/info/demographics* API in an *onHandler* hook for the */api/login* API, we'd need this defined in the *routes.json* file:
650
651 {
652 "uri": "/api/info/demographics",
653 "method": "GET",
654 "handler": "getDemographics",
655 "on_microservice": "info_service",
656 "from_microservices": [
657 "login_service"
658 ]
659 }
660
661
662An *onMSResponse* module is placed is along-side the API handler module, eg: for the API defined in **routes.json**:
663
664 {
665 "uri": "/api/login",
666 "method": "POST",
667 "handler": "login",
668 "on_microservice": "login_service",
669 "authenticate": false
670 }
671
672
673You would define an *onMSResponse* hook by adding the *onMSResponse.js* module file here:
674
675
676 ~/microserviceExample
677 |
678 |_ configuration
679 |
680 |_ login_service
681 | |
682 | login
683 | |
684 | |_ index.js
685 | |
686 | |_ onMSResponse.js
687
688
689Alternatively, if you had chosen to group all your API handler modules in an *apis* folder, you would place the *onMSResponse* module within the *apis* folder, eg:
690
691 ~/microserviceExample
692 |
693 |_ configuration
694 |
695 |_ login_service
696 | |
697 | apis
698 | |
699 | login
700 | |
701 | |_ index.js
702 | |
703 | |_ onMSResponse.js
704
705
706
707### Module structure
708
709Your *onMSResponse.js* file should export a function of the structure shown below:
710
711 module.exports = function(message, jwt, forward, sendBack) {
712 // onRequest logic here
713 };
714
715
716### Module Function Context
717
718The *onMSResponse* module's **this** object is the QEWD Master Process object for the MicroService.
719
720
721### Module Function Arguments
722
723#### message
724
725This is the response object you sent from your handler (ie the object you created as the *finished()* function's argument.
726
727It is also augmented by QEWD with some additional properties which are for internal use by QEWD and which should be left unchanged.
728
729#### jwt
730
731This is the updated JWT returned from your API handler.
732
733You can easily access values within the JWT using the built-in QEWD function:
734
735 this.jwt.handlers.getProperty(propertyName, jwt)
736
737where *propertyName* is the name of a property (or claim) within the JWT. For example:
738
739 var username = this.jwt.handlers.getProperty('username', jwt);
740
741
742#### forward
743
744This is the function that you should use for forwarding an API request. It is invoked like this:
745
746 forward(apiRequest, jwt, callback)
747
748The arguments are:
749
750- **apiRequest**: an object that defines an API request, eg:
751
752 *var apiRequest = {
753 path: '/api/info/demographics',
754 method: 'GET'
755 };*
756
757- **jwt**: the *jwt* argument as described above
758
759- **callback**: Callback function with a single argument - *responseObj* - containing the response object from the API
760
761
762#### sendBack
763
764This is the function that you should use to return your final response back to the *Orchestrator* (or other MicroService if it sent the request).
765
766It has a single argument: the object you wish to return.
767
768
769### Example onMSResponse Module
770
771This intercepts the response from the */api/login* handler and fetches demographics information before returning a combined response back to the *Orchestrator*
772
773 module.exports = function(message, jwt, forward, sendBack) {
774 var apiRequest = {
775 path: '/api/info/demographics',
776 method: 'GET'
777 };
778 forward(apiRequest, jwt, function(responseObj) {
779 sendBack({
780 login: message,
781 demographics: responseObj
782 });
783 });
784 };
785
786
787
788
789**Note**: If your *onMSResponse* function returns *false*, the original response object from your API handler will be returned to the *Orchestrator*. You can use this to conditionalise your *onMSResponse* logic, for example:
790
791
792 module.exports = function(message, jwt, forward, sendBack) {
793
794 if (message.item === 'bypass') {
795 return false; // Just let QEWD return the original response from your handler
796 }
797
798 var apiRequest = {
799 path: '/api/info/demographics',
800 method: 'GET'
801 };
802 forward(apiRequest, jwt, function(responseObj) {
803 sendBack({
804 login: message,
805 demographics: responseObj
806 });
807 });
808 };
809
810
811
812## onOrchResponse
813
814
815### Filename and Directory location
816
817The filename is **onOrchResponse.js**. Its name is case-sensitive.
818
819The *onOrchResponse* hook is only relevant to MicroService applications
820
821It is applied to the specified API, and is invoked on the QEWD Master process of the *Orchestrator* MicroService, on receipt of the response from the MicroService that handled the API.
822
823Note: the *onOrchResponse* hook is invoked before the *onWSResponse* hook (if defined).
824
825The *onOrchResponse* hook allows you to intercept the response from your API's handling MicroService, and send one or more further API requests, eg to other MicroServices, in order to build up a complex, composite response.
826
827The *onOrchResponse* hook is therefore similar to the *onMSResponse* hook, but the *onOrchResponse* hook is invoked on the *Orchestrator*, whilst the *onMSResponse* hook is available on the other MicroServices that make up your application. Which you use will depend on how and where you want to construct the results of complex chains of APIs.
828
829
830An *onOrchResponse* module is placed is along-side the API handler module, eg: for the API defined in **routes.json**:
831
832 {
833 "uri": "/api/login",
834 "method": "POST",
835 "handler": "login",
836 "on_microservice": "login_service",
837 "authenticate": false
838 }
839
840
841You would define an *onOrchResponse* hook by adding the *onOrchResponse.js* module file alongside the handler's *index.js* file, eg:
842
843
844 ~/microserviceExample
845 |
846 |_ configuration
847 |
848 |_ login_service
849 | |
850 | login
851 | |
852 | |_ index.js
853 | |
854 | |_ onOrchResponse.js
855
856
857Or, if you had grouped all your handler modules within an *apis* subfolder:
858
859
860 ~/microserviceExample
861 |
862 |_ configuration
863 |
864 |_ login_service
865 | |
866 | apis
867 | |
868 | login
869 | |
870 | |_ index.js
871 | |
872 | |_ onOrchResponse.js
873
874
875
876### Module structure
877
878Your *onOrchResponse.js* file should export a function of the structure shown below:
879
880 module.exports = function(responseObj, request, forwardToMS, sendResponse, getJWTproperty) {
881 // onOrchRequest logic here
882 };
883
884
885### Module Function Context
886
887The *onOrchResponse* module's **this** object is the QEWD Master Process object for the MicroService.
888
889
890### Module Function Arguments
891
892#### responseObj
893
894This is the object containing the response that has been received by the Orchestrator's Master process from another MicroService (or chain of MicroServices).
895
896**Note**: if an error has occurred, then *responseObj.message.error* will exist and contain a string value.
897
898#### request
899
900This is the object containing the original request that was received by the Orchestrator. It is made available because it may contain information that is useful for your hook's logic, eg *request.method* and *request.path* are the original incoming request's method and path respectively.
901
902#### forwardToMS
903
904This is a function that you can use to forward a new request to a MicroService, rather than, or before returning the response to the REST Client.
905
906It takes two arguments:
907
908- **routeObj**: an object that defines the API route that you want to invoke, in terms of its path and method. It can also include a *query* or *body* property (each of which is a sub-object);
909- **callback**: a function with a single argument - the response object from the MicroService that processed your API route information.
910
911For example:
912
913 var msg = {
914 path: '/api/info/info',
915 method: 'GET'
916 };
917 forwardToMS(msg, function(responseObj) {
918 // handle response
919 });
920
921
922#### sendResponse
923
924This is a function that you should use to return your final response object back to the REST Client.
925
926It has a single argument:
927
928- **responseObj**: The object containing the response that you want to return to the REST Client
929
930For example:
931
932 var msg = {
933 path: '/api/info/info',
934 method: 'GET'
935 };
936 forwardToMS(msg, function(responseObj) {
937 sendResponse(responseObj);
938 });
939 return true;
940
941
942Alternatively, your *onWSResponse* hook module may simply be used to re-package / re-format the response before sending it to the REST Client, eg:
943
944 if (!responseObj.message.error) {
945 var respObj = {
946 yousent: request.path,
947 usingMethod: request.method,
948 exp: getJWTProperty('exp')
949 };
950 sendResponse(respObj);
951 return true;
952 }
953
954**Note**: If you use the *sendResponse* function, your *onWSResponse* hook module **MUST** return *true*, as shown in the examples above. This tells QEWD that you are taking responsibility for returning the REST Client's response, and therefore QEWD doesn't try to also send a response.
955
956
957#### getJWTProperty
958
959This function allows you to extract a claim/property from the JWT.
960
961It has a single argument: the name of the JWT claim or property.
962
963See the example above, which gets the value of the *exp* property from the JWT.
964
965
966### Example onWSResponse Module
967
968
969 module.exports = function(responseObj, request, forwardToMS, sendResponse, getJWTProperty) {
970 if (!responseObj.message.error) {
971 var msg = {
972 path: '/api/info/info',
973 method: 'GET'
974 };
975 forwardToMS(msg, function(responseObj) {
976 sendResponse(responseObj);
977 });
978 return true;
979 }
980 // otherwise the original error message will be sent to the REST Client
981 };
982
983
984
985## onWSResponse
986
987### Filename and Directory location
988
989The filename is **onWSResponse.js**. Its name is case-sensitive.
990
991The *onWSResponse* hook is only relevant to:
992
993- a QEWD-Up Monolithic application;
994- the Orchestrator MicroService in a QEWD-Up MicroService application
995
996Its placement depends on what mode you are using and/or microservice you are specifying it for
997
998#### Monolith
999
1000 ~/dockerExample
1001 |
1002 |_ onWSResponse.js
1003 |
1004 |_ configuration
1005 |
1006 |_ apis
1007 |
1008
1009The *onWSResponse* hook is applied to *ALL* responses received by the Master process.
1010
1011
1012#### MicroService: Orchestrator
1013
1014 ~/microserviceExample
1015 |
1016 |_ configuration
1017 |
1018 |_ orchestrator
1019 | |
1020 | |_ onWSResponse.js
1021
1022
1023The *onWSResponse* hook is applied to *ALL* responses received by the Orchestrator's Master process.
1024
1025
1026### Module structure
1027
1028Your *onWSResponse.js* file should export a function of the structure shown below:
1029
1030 module.exports = function(req, res, next) {
1031 // perform onWSResponse processing
1032 next();
1033 };
1034
1035### Module Function Arguments
1036
1037#### req
1038
1039The WebServer (eg Express by default) request object
1040
1041#### res
1042
1043The WebServer (eg Express by default) response object
1044
1045#### next
1046
1047The WebServer (eg Express by default) next() function, allowing control to be passed to the next function in the middleware chain.
1048
1049### Example
1050
1051The following example does not, in fact, modify the response, but simply demonstrates how to implement a *pass-through* module that handles both success and error responses.
1052
1053**Notes**:
1054
1055- the response object can be found in *res.locals.message*;
1056- the original incoming API route path can be found in *req.originalUrl*: use this if you want the logic to be conditional for specific API routes.
1057
1058
1059 module.exports = function(req, res, next) {
1060 var messageObj = res.locals.message || {error: 'Not Found'};
1061 if (messageObj.error) {
1062 var code = 400;
1063 var status = messageObj.status;
1064 if (status && status.code) code = status.code;
1065 delete messageObj.status;
1066 delete messageObj.restMessage;
1067 delete messageObj.ewd_application;
1068 res.set('content-length', messageObj.length);
1069 res.status(code).send(messageObj);
1070 }
1071 else {
1072 res.send(messageObj);
1073 }
1074 next();
1075 };
1076
1077
1078
1079