UNPKG

107 kBMarkdownView Raw
1--------------------------------------------------
2<a name="0.13.5"></a>
3# [0.13.5](https://github.com/moleculerjs/moleculer/compare/v0.13.4...v0.13.5) (2018-12-09)
4
5# New
6
7## Conditional caching
8It's a common issue that you enable caching for an action but sometimes you don't want to get data from cache. To solve it you may set `ctx.meta.$cache = false` before calling and the cacher won't send cached responses.
9
10**Example**
11```js
12// Turn off caching for this request
13broker.call("greeter.hello", { name: "Moleculer" }, { meta: { $cache: false }}))
14```
15
16Other solution is that you use a custom function which enables or disables caching for every request. The function gets the `ctx` Context instance so it has access any params or meta data.
17
18**Example**
19```js
20// greeter.service.js
21module.exports = {
22 name: "greeter",
23 actions: {
24 hello: {
25 cache: {
26 enabled: ctx => ctx.params.noCache !== true,
27 keys: ["name"]
28 },
29 handler(ctx) {
30 this.logger.debug(chalk.yellow("Execute handler"));
31 return `Hello ${ctx.params.name}`;
32 }
33 }
34 }
35};
36
37// Use custom `enabled` function to turn off caching for this request
38broker.call("greeter.hello", { name: "Moleculer", noCache: true }))
39```
40
41## LRU memory cacher
42An LRU memory cacher has been added to the core modules. It uses the familiar [lru-cache](https://github.com/isaacs/node-lru-cache) library.
43
44**Example**
45```js
46let broker = new ServiceBroker({ cacher: "MemoryLRU" });
47```
48
49```js
50let broker = new ServiceBroker({
51 logLevel: "debug",
52 cacher: {
53 type: "MemoryLRU",
54 options: {
55 // Maximum items
56 max: 100,
57 // Time-to-Live
58 ttl: 3
59 }
60 }
61});
62```
63
64
65# Changes
66- throw the error further in `loadService` method so that Runner prints the correct error stack.
67- new `packetLogFilter` transit option to filter packets in debug logs (e.g. HEARTBEAT packets) by [@faeron](https://github.com/faeron)
68- the Redis cacher `clean` & `del` methods handle array parameter by [@dkuida](https://github.com/dkuida)
69- the Memory cacher `clean` & `del` methods handle array parameter by [@icebob](https://github.com/icebob)
70- fix to handle `version: 0` as a valid version number by [@ngraef](https://github.com/ngraef)
71
72--------------------------------------------------
73<a name="0.13.4"></a>
74# [0.13.4](https://github.com/moleculerjs/moleculer/compare/v0.13.3...v0.13.4) (2018-11-04)
75
76# Changes
77- catch errors in `getCpuUsage()` method.
78- support multiple urls in AMQP transporter by [@urossmolnik](https://github.com/urossmolnik)
79- fix AMQP connection recovery by [@urossmolnik](https://github.com/urossmolnik)
80- add `transit.disableReconnect` option to disable reconnecting logic at broker starting by [@Gadi-Manor](https://github.com/Gadi-Manor)
81- catch `os.userInfo` errors in health action by [@katsanva](https://github.com/katsanva)
82- allow specifying 0 as `retries` [#404](https://github.com/moleculerjs/moleculer/issues/404) by [@urossmolnik](https://github.com/urossmolnik)
83- fix `GraceFulTimeoutError` bug [#400](https://github.com/moleculerjs/moleculer/issues/400)
84- fix event return handling to avoid localEvent error handling issue in middleware [#403](https://github.com/moleculerjs/moleculer/issues/403)
85- update [fastest-validator](https://github.com/icebob/fastest-validator) to the 0.6.12 version
86- update all dependencies
87
88--------------------------------------------------
89<a name="0.13.3"></a>
90# [0.13.3](https://github.com/moleculerjs/moleculer/compare/v0.13.2...v0.13.3) (2018-09-27)
91
92# Changes
93- update dependencies
94- fix MQTTS connection string protocol from `mqtt+ssl://` to `mqtts://` by [@AndreMaz](https://github.com/AndreMaz)
95- Moleculer Runner supports typescript configuration file `moleculer.config.ts`
96- fix to call service start after hot-reloading.
97- fix Bluebird warning in service loading [#381](https://github.com/moleculerjs/moleculer/issues/381) by [@faeron](https://github.com/faeron)
98- fix `waitForServices` definition in `index.d.ts` [#358](https://github.com/moleculerjs/moleculer/issues/358)
99- fix `cpuUsage` issue [#379](https://github.com/moleculerjs/moleculer/issues/379) by [@faeron](https://github.com/faeron)
100
101
102--------------------------------------------------
103<a name="0.13.2"></a>
104# [0.13.2](https://github.com/moleculerjs/moleculer/compare/v0.13.1...v0.13.2) (2018-08-16)
105
106# Changes
107- update dependencies
108- add Notepack (other MsgPack) serializer
109- `skipProcessEventRegistration` broker option to disable `process.on` shutdown event handlers which stop broker.
110- make unique service dependencies
111- add `socketOptions` to AMQP transporter options. [#330](https://github.com/moleculerjs/moleculer/pull/330)
112- fix unhandled promise in AMQP transporter `connect` method.
113- add `autoDeleteQueues` option to AMQP transporter. [#341](https://github.com/moleculerjs/moleculer/pull/341)
114- ES6 support has improved. [#348](https://github.com/moleculerjs/moleculer/pull/348)
115- add `qos` transporter option to MQTT transporter. Default: `0`
116- add `topicSeparator` transporter option to MQTT transporter. Default: `.`
117- fix MQTT transporter disconnect logic (waiting for in-flight messages)
118- add support for non-defined defaultOptions variables [#350](https://github.com/moleculerjs/moleculer/pull/350)
119- update ioredis to v4
120
121--------------------------------------------------
122<a name="0.13.1"></a>
123# [0.13.1](https://github.com/moleculerjs/moleculer/compare/v0.13.0...v0.13.1) (2018-07-13)
124
125# Changes
126- improve `index.d.ts`
127- support Duplex streams [#325](https://github.com/moleculerjs/moleculer/issues/325)
128- update dependencies
129
130--------------------------------------------------
131<a name="0.13.0"></a>
132# [0.13.0](https://github.com/moleculerjs/moleculer/compare/v0.12.8...v0.13.0) (2018-07-08)
133
134**Migration guide from v0.12.x to v0.13.x is [here](docs/MIGRATION_GUIDE_0.13.md).**
135
136# Breaking changes
137
138## Streaming support
139Built-in streaming support has just been implemented. Node.js streams can be transferred as request `params` or as response. You can use it to transfer uploaded file from a gateway or encode/decode or compress/decompress streams.
140
141>**Why is it a breaking change?**
142>
143> Because the protocol has been extended with a new field and it caused a breaking change in schema-based serializators (ProtoBuf, Avro). Therefore, if you use ProtoBuf or Avro, you won't able to communicate with the previous (<=0.12) brokers. Using JSON or MsgPack serializer, there is nothing extra to do.
144
145### Examples
146
147**Send a file to a service as a stream**
148```js
149const stream = fs.createReadStream(fileName);
150
151broker.call("storage.save", stream, { meta: { filename: "avatar-123.jpg" }});
152```
153
154Please note, the `params` should be a stream, you cannot add any more variables to the request. Use the `meta` property to transfer additional data.
155
156**Receiving a stream in a service**
157```js
158module.exports = {
159 name: "storage",
160 actions: {
161 save(ctx) {
162 const s = fs.createWriteStream(`/tmp/${ctx.meta.filename}`);
163 ctx.params.pipe(s);
164 }
165 }
166};
167```
168
169**Return a stream as response in a service**
170```js
171module.exports = {
172 name: "storage",
173 actions: {
174 get: {
175 params: {
176 filename: "string"
177 },
178 handler(ctx) {
179 return fs.createReadStream(`/tmp/${ctx.params.filename}`);
180 }
181 }
182 }
183};
184```
185
186**Process received stream on the caller side**
187```js
188const filename = "avatar-123.jpg";
189broker.call("storage.get", { filename })
190 .then(stream => {
191 const s = fs.createWriteStream(`./${filename}`);
192 stream.pipe(s);
193 s.on("close", () => broker.logger.info("File has been received"));
194 })
195```
196
197**AES encode/decode example service**
198```js
199const crypto = require("crypto");
200const password = "moleculer";
201
202module.exports = {
203 name: "aes",
204 actions: {
205 encrypt(ctx) {
206 const encrypt = crypto.createCipher("aes-256-ctr", password);
207 return ctx.params.pipe(encrypt);
208 },
209
210 decrypt(ctx) {
211 const decrypt = crypto.createDecipher("aes-256-ctr", password);
212 return ctx.params.pipe(decrypt);
213 }
214 }
215};
216```
217
218## Better Service & Broker lifecycle handling
219The ServiceBroker & Service lifecycle handler logic has already been improved. The reason for amendment was a problem occuring during loading more services locally; they could call each others' actions before `started` execution. It generally causes errors if database connecting process started in the `started` event handler.
220
221This problem has been fixed with a probable side effect: causing errors (mostly in unit tests) if you call the local services without `broker.start()`.
222
223**It works in the previous version**
224```js
225const { ServiceBroker } = require("moleculer");
226
227const broker = new ServiceBroker();
228
229broker.loadService("./math.service.js");
230
231broker.call("math.add", { a: 5, b: 3 }).then(res => console.log);
232// Prints: 8
233```
234From v0.13 it throws a `ServiceNotFoundError` exception, because the service is only loaded but not started yet.
235
236**Correct logic**
237```js
238const { ServiceBroker } = require("moleculer");
239
240const broker = new ServiceBroker();
241
242broker.loadService("./math.service.js");
243
244broker.start().then(() => {
245 broker.call("math.add", { a: 5, b: 3 }).then(res => console.log);
246 // Prints: 8
247});
248```
249
250or with await
251
252```js
253broker.loadService("./math.service.js");
254
255await broker.start();
256
257const res = await broker.call("math.add", { a: 5, b: 3 });
258console.log(res);
259// Prints: 8
260```
261
262Similar issue has been fixed at broker shutdown. Previously when you stopped a broker, which while started to stop local services, it still acccepted incoming requests from remote nodes.
263
264The shutdown logic has also been changed. When you call `broker.stop`, at first broker publishes an empty service list to remote nodes, so they route the requests to other instances.
265
266
267## Default console logger
268No longer need to set `logger: console` in broker options, because ServiceBroker uses `console` as default logger.
269
270```js
271const broker = new ServiceBroker();
272// It will print log messages to the console
273```
274
275**Disable loggging (e.g. in tests)**
276```js
277const broker = new ServiceBroker({ logger: false });
278```
279
280## Changes in internal event sending logic
281The `$` prefixed internal events will be transferred if they are called by `emit` or `broadcast`. If you don't want to transfer them, use the `broadcastLocal` method.
282
283> From v0.13, the `$` prefixed events mean built-in core events instead of internal "only-local" events.
284
285## Improved Circuit Breaker
286Threshold-based circuit-breaker solution has been implemented. It uses a time window to check the failed request rate. Once the `threshold` value is reached, it trips the circuit breaker.
287
288```js
289const broker = new ServiceBroker({
290 nodeID: "node-1",
291 circuitBreaker: {
292 enabled: true,
293 threshold: 0.5,
294 minRequestCount: 20,
295 windowTime: 60, // in seconds
296 halfOpenTime: 5 * 1000,
297 check: err => err && err.code >= 500
298 }
299});
300```
301
302Instead of `failureOnTimeout` and `failureOnReject` properties, there is a new `check()` function property in the options. It is used by circuit breaker in order to detect which error is considered as a failed request.
303
304You can override these global options in action definition, as well.
305
306```js
307module.export = {
308 name: "users",
309 actions: {
310 create: {
311 circuitBreaker: {
312 // All CB options can be overwritten from broker options.
313 threshold: 0.3,
314 windowTime: 30
315 },
316 handler(ctx) {}
317 }
318 }
319};
320```
321
322### CB metrics events removed
323The metrics circuit breaker events have been removed due to internal event logic changes.
324Use the `$circuit-breaker.*` events instead of `metrics.circuit-breaker.*` events.
325
326## Improved Retry feature (with exponential backoff)
327The old retry feature has been improved. Now it uses exponential backoff for retries. The old solution retries the request immediately in failures.
328The retry options have also been changed in the broker options. Every option is under the `retryPolicy` property.
329
330```js
331const broker = new ServiceBroker({
332 nodeID: "node-1",
333 retryPolicy: {
334 enabled: true,
335 retries: 5,
336 delay: 100,
337 maxDelay: 2000,
338 factor: 2,
339 check: err => err && !!err.retryable
340 }
341});
342```
343
344**Overwrite the `retries` value in calling option**
345The `retryCount` calling options has been renamed to `retries`.
346
347```js
348broker.call("posts.find", {}, { retries: 3 });
349```
350
351There is a new `check()` function property in the options. It is used by the Retry middleware in order to detect which error is a failed request and needs a retry. The default function checks the `retryable` property of errors.
352
353These global options can be overridden in action definition, as well.
354
355```js
356module.export = {
357 name: "users",
358 actions: {
359 find: {
360 retryPolicy: {
361 // All Retry policy options can be overwritten from broker options.
362 retries: 3,
363 delay: 500
364 },
365 handler(ctx) {}
366 },
367 create: {
368 retryPolicy: {
369 // Disable retries for this action
370 enabled: false
371 },
372 handler(ctx) {}
373 }
374 }
375};
376```
377
378## Changes in context tracker
379There are also some changes in context tracker configuration.
380
381```js
382const broker = new ServiceBroker({
383 nodeID: "node-1",
384 tracking: {
385 enabled: true,
386 shutdownTimeout: 5000
387 }
388});
389```
390
391**Disable tracking in calling option at calling**
392
393```js
394broker.call("posts.find", {}, { tracking: false });
395```
396
397_The shutdown timeout can be overwritten by `$shutdownTimeout` property in service settings._
398
399
400## Removed internal statistics module
401The internal statistics module (`$node.stats`) is removed. Yet you need it, download from [here](https://gist.github.com/icebob/99dc388ee29ae165f879233c2a9faf63), load as a service and call the `stat.snapshot` to receive the collected statistics.
402
403## Renamed errors
404Some errors have been renamed in order to follow name conventions.
405- `ServiceNotAvailable` -> `ServiceNotAvailableError`
406- `RequestRejected` -> `RequestRejectedError`
407- `QueueIsFull` -> `QueueIsFullError`
408- `InvalidPacketData` -> `InvalidPacketDataError`
409
410## Context nodeID changes
411The `ctx.callerNodeID` has been removed. The `ctx.nodeID` contains the target or caller nodeID. If you need the current nodeID, use `ctx.broker.nodeID`.
412
413## Enhanced ping method
414It returns `Promise` with results of ping responses. Moreover, the method is renamed to `broker.ping`.
415
416**Ping a node with 1sec timeout**
417```js
418broker.ping("node-123", 1000).then(res => broker.logger.info(res));
419```
420Output:
421```js
422{
423 nodeID: 'node-123',
424 elapsedTime: 16,
425 timeDiff: -3
426}
427```
428
429**Ping all known nodes**
430```js
431broker.ping().then(res => broker.logger.info(res));
432```
433Output:
434```js
435{
436 "node-100": {
437 nodeID: 'node-100',
438 elapsedTime: 10,
439 timeDiff: -2
440 } ,
441 "node-101": {
442 nodeID: 'node-101',
443 elapsedTime: 18,
444 timeDiff: 32
445 },
446 "node-102": {
447 nodeID: 'node-102',
448 elapsedTime: 250,
449 timeDiff: 850
450 }
451}
452```
453
454## Amended cacher key generation logic
455When you didn't define `keys` at caching, the cacher hashed the whole `ctx.params` and used as a key to store the content. This method was too slow and difficult to implement to other platforms. Therefore we have changed it. The new method is simpler, the key generator concatenates all property names & values from `ctx.params`.
456
457However, the problem with this new logic is that the key can be very long. It can cause performance issues when you use too long keys to get or save cache entries. To avoid it, there is a `maxParamsLength` option to limit the key length. If it is longer than the configured limit, the cacher calculates a hash (SHA256) from the full key and add it to the end of key.
458
459> The minimum of `maxParamsLength` is `44` (SHA 256 hash length in Base64).
460>
461> To disable this feature, set it to `0` or `null`.
462
463**Generate a full key from the whole params**
464```js
465cacher.getCacheKey("posts.find", { id: 2, title: "New post", content: "It can be very very looooooooooooooooooong content. So this key will also be too long" });
466// Key: 'posts.find:id|2|title|New post|content|It can be very very looooooooooooooooooong content. So this key will also be too long'
467```
468
469**Generate a limited key with hash**
470```js
471const broker = new ServiceBroker({
472 logger: console,
473 cacher: {
474 type: "Memory",
475 options: {
476 maxParamsLength: 60
477 }
478 }
479});
480
481cacher.getCacheKey("posts.find", { id: 2, title: "New post", content: "It can be very very looooooooooooooooooong content. So this key will also be too long" });
482// Key: 'posts.find:id|2|title|New pL4ozUU24FATnNpDt1B0t1T5KP/T5/Y+JTIznKDspjT0='
483```
484
485Of course, you can use your custom solution with `keygen` cacher options like earlier.
486
487## Cacher matcher changed
488The cacher matcher code also changed in `cacher.clean` method. The previous (wrong) matcher couldn't handle dots (.) properly in patterns. E.g the `posts.*` pattern cleaned the `posts.find.something` keys, too. Now it has been fixed, but it means that you should use `posts.**` pattern because the `params` and `meta` values can contain dots.
489
490## Changed Moleculer errors signature
491The following Moleculer Error classes constructor arguments is changed to `constructor(data)`:
492- `ServiceNotFoundError`
493- `ServiceNotAvailableError`
494- `RequestTimeoutError`
495- `RequestSkippedError`
496- `RequestRejectedError`
497- `QueueIsFullError`
498- `MaxCallLevelError`
499- `ProtocolVersionMismatchError`
500- `InvalidPacketDataError`
501
502**Before**
503```js
504throw new ServiceNotFoundError("posts.find", "node-123");
505```
506
507**Now**
508```js
509throw new ServiceNotFoundError({ action: "posts.find", nodeID: "node-123" });
510```
511
512
513# New
514
515## New state-of-the-art middlewares
516We have been improved the current middleware handler and enriched it with a lot of useful features. As a result, you can hack more internal flow logic with custom middlewares (e.g. event sending, service creating, service starting...etc)
517
518The new one is an `Object` with hooks instead of a simple `Function`. However, the new solution is backward compatible, so you don't need to migrate your old middlewares.
519
520**A new middleware with all available hooks**
521```js
522const MyCustomMiddleware = {
523 // Wrap local action handlers (legacy middleware handler)
524 localAction(next, action) {
525
526 },
527
528 // Wrap remote action handlers
529 remoteAction(next, action) {
530
531 },
532
533 // Wrap local event handlers
534 localEvent(next, event) {
535
536 }
537
538 // Wrap broker.createService method
539 createService(next) {
540
541 }
542
543 // Wrap broker.destroyService method
544 destroyService(next) {
545
546 }
547
548 // Wrap broker.call method
549 call(next) {
550
551 }
552
553 // Wrap broker.mcall method
554 mcall(next) {
555
556 }
557
558 // Wrap broker.emit method
559 emit(next) {
560
561 },
562
563 // Wrap broker.broadcast method
564 broadcast(next) {
565
566 },
567
568 // Wrap broker.broadcastLocal method
569 broadcastLocal(next) {
570
571 },
572
573 // After a new local service created (sync)
574 serviceCreated(service) {
575
576 },
577
578 // Before a local service started (async)
579 serviceStarting(service) {
580
581 },
582
583 // After a local service started (async)
584 serviceStarted(service) {
585
586 },
587
588 // Before a local service stopping (async)
589 serviceStopping(service) {
590
591 },
592
593 // After a local service stopped (async)
594 serviceStopped(service) {
595
596 },
597
598 // After broker is created (async)
599 created(broker) {
600
601 },
602
603 // Before broker starting (async)
604 starting(broker) {
605
606 },
607
608 // After broker started (async)
609 started(broker) {
610
611 },
612
613 // Before broker stopping (async)
614 stopping(broker) {
615
616 },
617
618 // After broker stopped (async)
619 stopped(broker) {
620
621 }
622}
623```
624
625**Use it in broker options**
626```js
627const broker = new ServiceBroker({
628 middlewares: [
629 MyCustomMiddleware
630 ]
631});
632```
633
634### Wrapping handlers
635Some hooks are wrappers. It means you need to wrap the original handler and return a new Function.
636Wrap hooks where the first parameter is `next`.
637
638**Wrap local action handler**
639```js
640const MyDoSomethingMiddleware = {
641 localAction(next, action) {
642 if (action.myFunc) {
643 // Wrap the handler
644
645 return function(ctx) {
646 doSomethingBeforeHandler(ctx);
647
648 return handler(ctx)
649 .then(res => {
650 doSomethingAfterHandler(res);
651 // Return the original result
652 return res;
653 })
654 .catch(err => {
655 doSomethingAfterHandlerIfFailed(err);
656
657 // Throw further the error
658 throw err;
659 });
660 }
661 }
662
663 // If the feature is disabled we don't wrap it, return the original handler
664 // So it won't cut down the performance for actions where the feature is disabled.
665 return handler;
666 }
667};
668```
669
670### Decorate broker (to extend functions)
671Other hooks are to help you to decorate new features in ServiceBroker & services.
672
673**Decorate broker with a new `allCall` method**
674```js
675const broker = new ServiceBroker({
676 middlewares: [
677 {
678 // After broker is created
679 created(broker) {
680 // Call action on all available nodes
681 broker.allCall = function(action, params, opts = {}) {
682 const nodeIDs = this.registry.getNodeList({ onlyAvailable: true })
683 .map(node => node.id);
684
685 // Make direct call to the given Node ID
686 return Promise.all(nodeIDs.map(nodeID => broker.call(action, params, Object.assign({ nodeID }, opts))));
687 }
688 }
689 }
690 ]
691});
692
693await broker.start();
694
695// Call `$node.health` on every nodes & collect results
696const res = await broker.allCall("$node.health");
697```
698
699**Decorate services with a new method**
700```js
701const broker = new ServiceBroker({
702 middlewares: [
703 {
704 // After a new local service created
705 serviceCreated(service) {
706 // Call action on all available nodes
707 service.customFunc = function() {
708 // Do something
709 }.bind(service);
710 }
711 }
712 ]
713});
714```
715
716In service schema:
717
718```js
719module.export = {
720 name: "users",
721 actions: {
722 find(ctx) {
723 // Call the new custom function
724 this.customFunc();
725 }
726 }
727};
728```
729
730> The mixins can do similar things, so we prefer mixins to this decorating.
731
732## Many internal features are exposed to internal middlewares
733Due to the new advanced middlewares, we could bring out many integrated features to middlewares. They are available under `require("moleculer").Middlewares` property, but they load automatically.
734
735**New internal middlewares:**
736- Action hook handling
737- Validator
738- Bulkhead
739- Cacher
740- Context tracker
741- Circuit Breaker
742- Timeout
743- Retry
744- Fallback
745- Error handling
746- Metrics
747
748> Turn off the automatic loading with `internalMiddlewares: false` broker option. In this case you have to add them to `middlewares: []` broker option.
749
750> The `broker.use` method is deprecated. Use `middlewares: []` in the broker options instead.
751
752## Action hooks
753Define action hooks to wrap certain actions coming from mixins.
754There are `before`, `after` and `error` hooks. Assign it to a specified action or all actions (`*`) in service.
755The hook can be a `Function` or a `String`. The latter must be a local service method name.
756
757**Before hooks**
758
759```js
760const DbService = require("moleculer-db");
761
762module.exports = {
763 name: "posts",
764 mixins: [DbService]
765 hooks: {
766 before: {
767 // Define a global hook for all actions
768 // The hook will call the `resolveLoggedUser` method.
769 "*": "resolveLoggedUser",
770
771 // Define multiple hooks
772 remove: [
773 function isAuthenticated(ctx) {
774 if (!ctx.user)
775 throw new Error("Forbidden");
776 },
777 function isOwner(ctx) {
778 if (!this.checkOwner(ctx.params.id, ctx.user.id))
779 throw new Error("Only owner can remove it.");
780 }
781 ]
782 }
783 },
784
785 methods: {
786 async resolveLoggedUser(ctx) {
787 if (ctx.meta.user)
788 ctx.user = await ctx.call("users.get", { id: ctx.meta.user.id });
789 }
790 }
791}
792```
793
794**After & Error hooks**
795
796```js
797const DbService = require("moleculer-db");
798
799module.exports = {
800 name: "users",
801 mixins: [DbService]
802 hooks: {
803 after: {
804 // Define a global hook for all actions to remove sensitive data
805 "*": function(ctx, res) {
806 // Remove password
807 delete res.password;
808
809 // Please note, must return result (either the original or a new)
810 return res;
811 },
812 get: [
813 // Add a new virtual field to the entity
814 async function (ctx, res) {
815 res.friends = await ctx.call("friends.count", { query: { follower: res._id }});
816
817 return res;
818 },
819 // Populate the `referrer` field
820 async function (ctx, res) {
821 if (res.referrer)
822 res.referrer = await ctx.call("users.get", { id: res._id });
823
824 return res;
825 }
826 ]
827 },
828 error: {
829 // Global error handler
830 "*": function(ctx, err) {
831 this.logger.error(`Error occurred when '${ctx.action.name}' action was called`, err);
832
833 // Throw further the error
834 throw err;
835 }
836 }
837 }
838};
839```
840
841The recommended use case is to create mixins filling up the service with methods and in `hooks` set method names.
842
843**Mixin**
844```js
845module.exports = {
846 methods: {
847 checkIsAuthenticated(ctx) {
848 if (!ctx.meta.user)
849 throw new Error("Unauthenticated");
850 },
851 checkUserRole(ctx) {
852 if (ctx.action.role && ctx.meta.user.role != ctx.action.role)
853 throw new Error("Forbidden");
854 },
855 checkOwner(ctx) {
856 // Check the owner of entity
857 }
858 }
859}
860```
861
862**Use mixin methods in hooks**
863```js
864const MyAuthMixin = require("./my.mixin");
865
866module.exports = {
867 name: "posts",
868 mixins: [MyAuthMixin]
869 hooks: {
870 before: {
871 "*": ["checkIsAuthenticated"],
872 create: ["checkUserRole"],
873 update: ["checkUserRole", "checkOwner"],
874 remove: ["checkUserRole", "checkOwner"]
875 }
876 },
877
878 actions: {
879 find: {
880 // No required role
881 handler(ctx) {}
882 },
883 create: {
884 role: "admin",
885 handler(ctx) {}
886 },
887 update: {
888 role: "user",
889 handler(ctx) {}
890 }
891 }
892};
893```
894
895## New Bulkhead fault-tolerance feature
896Bulkhead feature is an internal middleware in Moleculer. Use it to control the concurrent request handling of actions.
897
898**Global settings in the broker options.** _Applied to all registered local actions._
899```js
900const broker = new ServiceBroker({
901 bulkhead: {
902 enabled: true,
903 concurrency: 3,
904 maxQueueSize: 10,
905 }
906});
907```
908
909The `concurrency` value restricts the concurrent request executions. If `maxQueueSize` is bigger than 0, broker queues additional requests, if all slots are taken. If queue size reaches `maxQueueSize` limit or it is 0, broker will throw `QueueIsFull` error for every addition request.
910
911These global options can be overriden in action definition, as well.
912
913```js
914module.export = {
915 name: "users",
916 actions: {
917 find: {
918 bulkhead: {
919 enabled: false
920 },
921 handler(ctx) {}
922 },
923 create: {
924 bulkhead: {
925 // Increment the concurrency value
926 // for this action
927 concurrency: 10
928 },
929 handler(ctx) {}
930 }
931 }
932};
933```
934
935## Fallback in action definition
936Due to the exposed Fallback middleware, fallback response can be set in the action definition, too.
937
938> Please note, this fallback response will only be used if the error occurs within action handler. If the request is called from a remote node and the request is timed out on the remote node, the fallback response is not be used. In this case, use the `fallbackResponse` in calling option.
939
940**Fallback as function**
941```js
942module.exports = {
943 name: "recommends",
944 actions: {
945 add: {
946 fallback: (ctx, err) => "Some cached result",
947 //fallback: "fakeResult",
948 handler(ctx) {
949 // Do something
950 }
951 }
952 }
953};
954```
955
956**Fallback as method name string**
957```js
958module.exports = {
959 name: "recommends",
960 actions: {
961 add: {
962 // Call the 'getCachedResult' method when error occurred
963 fallback: "getCachedResult",
964 handler(ctx) {
965 // Do something
966 }
967 }
968 },
969
970 methods: {
971 getCachedResult(ctx, err) {
972 return "Some cached result";
973 }
974 }
975};
976```
977
978## Action visibility
979The action has a new `visibility` property to control the visibility & callability of service actions.
980
981**Available values:**
982- `published` or `null`: public action. It can be called locally, remotely and can be published via API Gateway
983- `public`: public action, can be called locally & remotely but not published via API GW
984- `protected`: can be called only locally (from local services)
985- `private`: can be called only internally (via `this.actions.xy()` within service)
986
987```js
988module.exports = {
989 name: "posts",
990 actions: {
991 // It's published by default
992 find(ctx) {},
993 clean: {
994 // Callable only via `this.actions.clean`
995 visibility: "private",
996 handler(ctx) {}
997 }
998 },
999 methods: {
1000 cleanEntities() {
1001 // Call the action directly
1002 return this.actions.clean();
1003 }
1004 }
1005}
1006```
1007
1008> The default value is `null` (means `published`) due to backward compatibility.
1009
1010## New Thrift serializer
1011There is a new built-in [Thrift](http://thrift.apache.org/) serializer.
1012
1013```js
1014const broker = new ServiceBroker({
1015 serializer: "Thrift"
1016});
1017```
1018> To use this serializer install the `thrift` module with `npm install thrift --save` command.
1019
1020## Enhanced log level configuration
1021A new module-based log level configuration was added. The log level can be set for every Moleculer module. Use of wildcard is allowed.
1022
1023```js
1024const broker = new ServiceBroker({
1025 logger: console,
1026 logLevel: {
1027 "MY.**": false, // Disable logs
1028 "TRANS": "warn", // Only 'warn ' and 'error' log entries
1029 "*.GREETER": "debug", // All log entries
1030 "**": "debug", // All other modules use this level
1031 }
1032});
1033```
1034
1035**Please note, it works only with default console logger. In case of external loggers (Pino, Windows, Bunyan, ...etc), these log levels must be applied.**
1036
1037> These settings are evaluated from top to bottom, so the `**` level must be the last property.
1038
1039> Internal modules: `BROKER`, `TRANS`, `TX` as transporter, `CACHER`, `REGISTRY`.
1040>
1041> For services, the name comes from the service name. E.g. `POSTS`.
1042> A version is used as a prefix. E.g. `V2.POSTS`
1043
1044The old global log level settings works, as well.
1045```js
1046const broker = new ServiceBroker({
1047 logger: console,
1048 logLevel: "warn"
1049});
1050```
1051
1052## New `short` log formatter
1053A new `short` log formatter was also added. It is similar to the default, but doesn't print the date and `nodeID`.
1054
1055```js
1056const broker = new ServiceBroker({
1057 logFormatter: "short"
1058});
1059```
1060
1061**Output**
1062```
1063[19:42:49.055Z] INFO MATH: Service started.
1064```
1065
1066## Load services also with glob patterns
1067Moleculer Runner loads services also from glob patterns. It is useful when loading all services except certain ones.
1068
1069```bash
1070$ moleculer-runner services !services/others/**/*.service.js services/others/mandatory/main.service.js
1071```
1072
1073**Explanations:**
1074- `services` - legacy mode. Load all services from the `services` folder with `**/*.service.js` file mask
1075- `!services/others/**/*.service.js` - skip all services in the `services/others` folder and sub-folders.
1076- `services/others/mandatory/main.service.js` - load the exact service
1077
1078> Glob patterns work in the `SERVICES` enviroment variables, as well.
1079
1080## MemoryCacher cloning
1081There is a new `clone` property in the `MemoryCacher` options. If it's `true`, the cacher clones the cached data before returning.
1082If received value is modified, enable this option. Note: it cuts down the performance.
1083
1084**Enable cloning**
1085```js
1086const broker = new ServiceBroker({
1087 cacher: {
1088 type: "Memory",
1089 options: {
1090 clone: true
1091 }
1092 }
1093});
1094```
1095
1096This feature uses the lodash `_.cloneDeep` method. To change cloning method set a `Function` to the `clone` option instead of a `Boolean`.
1097
1098**Custom clone function with JSON parse & stringify:**
1099```js
1100const broker = new ServiceBroker({
1101 cacher: {
1102 type: "Memory",
1103 options: {
1104 clone: data => JSON.parse(JSON.stringify(data))
1105 }
1106 }
1107});
1108```
1109
1110# Changes
1111- service instances has a new property named `fullName` containing service version & service name.
1112- the `Action` has a `rawName` property containing action name without service name.
1113- new `$node.options` internal action to get the current broker options.
1114- `Context.create` & `new Context` signature changed.
1115- removed Context metrics methods. All metrics feature moved to the `Metrics` middleware.
1116- `ctx.timeout` moved to `ctx.options.timeout`.
1117- removed `ctx.callerNodeID`.
1118- `ctx.endpoint` is a new property pointing to target `Endpoint`. For example you can check with `ctx.endpoint.local` flag whether the request is remote or local.
1119- lazily generated `ctx.id`, i.e. only generated at access. `ctx.generateID()` was removed.
1120- renamed service lifecycle methods in service instances (not in service schema!)
1121- extended `transit.stat.packets` with byte-based statistics.
1122- `utils.deprecate` method was created for deprecation.
1123- Transporter supports `mqtt+ssl://`, `rediss://` & `amqps://` protocols in connection URIs.
1124- fixed circular objects handling in service schema (e.g.: Joi validator problem)
1125
1126# Deprecations
1127
1128- `broker.use()` has been deprecated. Use `middlewares: [...]` in broker options instead.
1129
1130--------------------------------------------------
1131<a name="0.12.8"></a>
1132# [0.12.8](https://github.com/moleculerjs/moleculer/compare/v0.12.6...v0.12.8) (2018-06-14)
1133
1134# Changes
1135- fix action disabling with mixins [#298](https://github.com/moleculerjs/moleculer/issues/298)
1136- Fix metrics options and add findNextActionEndpoint to index.d.ts
1137- update dependencies
1138- set `maxReconnectAttempts` to `-1` in NATS client to try reconnecting continuously
1139
1140--------------------------------------------------
1141<a name="0.12.6"></a>
1142# [0.12.6](https://github.com/moleculerjs/moleculer/compare/v0.12.5...v0.12.6) (2018-06-07)
1143
1144# Changes
1145- update dependencies
1146- The `breakLength` is changed to `Infinity` (single-line printing) for better log processing when logger prints objects and arrays.
1147- adds ability to customise console object/array printing [#285](https://github.com/moleculerjs/moleculer/issues/285)
1148 ```js
1149 const util = require("util");
1150
1151 const broker = new ServiceBroker({
1152 logger: true,
1153 logObjectPrinter: o => util.inspect(o, { depth: 4, colors: false, breakLength: 50 }) // `breakLength: 50` activates multi-line object
1154 });
1155 ```
1156
1157--------------------------------------------------
1158<a name="0.12.5"></a>
1159# [0.12.5](https://github.com/moleculerjs/moleculer/compare/v0.12.4...v0.12.5) (2018-05-21)
1160
1161# Changes
1162- fix AMQP logs. [#270](https://github.com/moleculerjs/moleculer/issues/270)
1163- fix transferred retryable error handling
1164- `broker.createService` supports ES6 classes
1165- fix broken promise chain if trackContext is enabled
1166
1167--------------------------------------------------
1168<a name="0.12.4"></a>
1169# [0.12.4](https://github.com/moleculerjs/moleculer/compare/v0.12.3...v0.12.4) (2018-05-10)
1170
1171# New
1172
1173## Graceful shutdown
1174Thanks for [@rmccallum81](https://github.com/rmccallum81), ServiceBroker supports graceful shutdown. You can enable it with `trackContext` broker option. If you enable it, all services wait for all running contexts before shutdowning. You can also define a timeout value with `gracefulStopTimeout` broker option.
1175
1176```js
1177const broker = new ServiceBroker({
1178 trackContext: true,
1179 gracefulStopTimeout: 5 * 1000 // waiting max 5 sec
1180});
1181```
1182
1183_This timeout can be overwrite in service settings with `$gracefulStopTimeout` property._
1184
1185# Changes
1186- fix service registry update after reconnecting. [#262](https://github.com/moleculerjs/moleculer/issues/262)
1187- update index.d.ts
1188- update dependencies
1189- fix distributed timeout handling
1190
1191--------------------------------------------------
1192<a name="0.12.3"></a>
1193# [0.12.3](https://github.com/moleculerjs/moleculer/compare/v0.12.2...v0.12.3) (2018-04-19)
1194
1195# Changes
1196- fix empty service mixins issue (`mixins: []`).
1197- update index.d.ts
1198
1199--------------------------------------------------
1200<a name="0.12.2"></a>
1201# [0.12.2](https://github.com/moleculerjs/moleculer/compare/v0.12.0...v0.12.2) (2018-04-11)
1202
1203# New
1204
1205## Latency strategy
1206This strategy selects a node which has the lowest latency, measured by periodic `PING`. Notice that the strategy only ping one of nodes from a single host. Due to the node list can be very long, it gets samples and selects the host with the lowest latency from only samples instead of the whole node list.
1207
1208**Usage**
1209```js
1210let broker = new ServiceBroker({
1211 registry: {
1212 strategy: "Latency"
1213 }
1214});
1215```
1216
1217**Strategy options**
1218
1219| Name | Type | Default | Description |
1220| ---- | ---- | --------| ----------- |
1221| `sampleCount` | `Number` | `5` | the number of samples. If you have a lot of hosts/nodes, it's recommended to *increase* the value. |
1222| `lowLatency` | `Number` | `10` | the low latency (ms). The node which has lower latency than this value is selected immediately. |
1223| `collectCount` | `Number` | `5` | the number of measured latency per host to keep in order to calculate the average latency. |
1224| `pingInterval` | `Number` | `10` | ping interval (s). If you have a lot of host/nodes, it's recommended to *increase* the value. |
1225
1226**Usage with custom options**
1227```js
1228let broker = new ServiceBroker({
1229 registry: {
1230 strategy: "Latency",
1231 strategyOptions: {
1232 sampleCount: 15,
1233 lowLatency: 20,
1234 collectCount: 10,
1235 pingInterval: 15
1236 }
1237 }
1238});
1239```
1240
1241## Filemask for Moleculer Runner
1242There is a new Moleculer Runner option `--mask` to define filemask when load all services from folders.
1243
1244**Example**
1245```
1246$ moleculer-runner.js -r --mask **/user*.service.js examples
1247```
1248
1249**Example to load Typescript services**
1250```
1251$ node -r ts-node/register node_modules/moleculer/bin/moleculer-runner --hot --repl --mask **/*.service.ts services
1252```
1253
1254# Changes
1255- fix `d.ts` issues
1256- fix event `group` handling in mixins ([#217](https://github.com/moleculerjs/moleculer/issues/217))
1257- move `mergeSchemas` from `utils` to `Service` static method. It can be overwritten in a custom ServiceFactory
1258- improve `d.ts`
1259- fix `prefix` option in Redis Cacher ([223](https://github.com/moleculerjs/moleculer/issues/223))
1260- remove `nanomatch` dependency, use own implementation
1261- fix ContextFactory issue ([235](https://github.com/moleculerjs/moleculer/issues/235))
1262- expose utility functions as `require("moleculer").Utils`
1263- overwritable `mergeSchemas` static method in `Service` class.
1264- Moleculer Runner precedence order is changed. The `SERVICES` & `SERVICEDIR` env vars overwrites the paths in CLI arguments.
1265
1266--------------------------------------------------
1267<a name="0.12.0"></a>
1268# [0.12.0](https://github.com/moleculerjs/moleculer/compare/v0.11.10...v0.12.0) (2018-03-03)
1269
1270This version contains the most changes in the history of Moleculer! More than 200 commits with 17k additions and a lot of new features.
1271
1272# Breaking changes
1273
1274## Github organization is renamed
1275The Github organization name (Ice Services) has been renamed to MoleculerJS. Please update your bookmarks.
1276
1277* Github organization: https://github.com/moleculerjs
1278* Website: https://moleculer.services or http://moleculerjs.com/
1279* Gitter chat: https://gitter.im/moleculerjs/moleculer
1280
1281## Mixin merging logic is changed
1282To support [#188](https://github.com/moleculerjs/moleculer/issues/188), mixin merging logic is changed at `actions`. Now it uses `defaultsDeep` for merging. It means you can extend the actions definition of mixins, no need to redeclare the `handler`.
1283
1284**Add extra action properties but `handler` is untouched**
1285```js
1286 // mixin.service.js
1287 module.exports = {
1288 actions: {
1289 create(ctx) {
1290 // Action handler without `params`
1291 }
1292 }
1293 };
1294```
1295
1296```js
1297 // my.service.js
1298 module.exports = {
1299 mixins: [MixinService]
1300 actions: {
1301 create: {
1302 // Add only `params` property to the `create` action
1303 // The handler is merged from mixin
1304 params: {
1305 name: "string"
1306 }
1307 }
1308 }
1309
1310 };
1311```
1312
1313## Wrapper removed from transporter options
1314If you are using transporter options, you will need to migrate them. The transporter specific wrapper has been removed from options (`nats`, `redis`, `mqtt`, `amqp`).
1315
1316**Before**
1317```js
1318// NATS transporter
1319const broker = new ServiceBroker({
1320 transporter: {
1321 type: "NATS",
1322 options: {
1323 nats: {
1324 user: "admin",
1325 pass: "1234"
1326 }
1327 }
1328 }
1329});
1330
1331// Redis transporter
1332const broker = new ServiceBroker({
1333 transporter: {
1334 type: "Redis",
1335 options: {
1336 redis: {
1337 port: 6379,
1338 db: 0
1339 }
1340 }
1341 }
1342});
1343
1344// MQTT transporter
1345const broker = new ServiceBroker({
1346 transporter: {
1347 type: "MQTT",
1348 options: {
1349 mqtt: {
1350 user: "admin",
1351 pass: "1234"
1352 }
1353 }
1354 }
1355});
1356
1357// AMQP transporter
1358const broker = new ServiceBroker({
1359 transporter: {
1360 type: "AMQP",
1361 options: {
1362 amqp: {
1363 prefetch: 1
1364 }
1365 }
1366 }
1367});
1368```
1369
1370**After**
1371```js
1372// NATS transporter
1373const broker = new ServiceBroker({
1374 transporter: {
1375 type: "NATS",
1376 options: {
1377 user: "admin",
1378 pass: "1234"
1379 }
1380 }
1381});
1382
1383// Redis transporter
1384const broker = new ServiceBroker({
1385 transporter: {
1386 type: "Redis",
1387 options: {
1388 port: 6379,
1389 db: 0
1390 }
1391 }
1392});
1393
1394// MQTT transporter
1395const broker = new ServiceBroker({
1396 transporter: {
1397 type: "MQTT",
1398 options: {
1399 user: "admin",
1400 pass: "1234"
1401 }
1402 }
1403});
1404
1405// AMQP transporter
1406const broker = new ServiceBroker({
1407 transporter: {
1408 type: "AMQP",
1409 options: {
1410 prefetch: 1
1411 }
1412 }
1413});
1414```
1415
1416## Default `nodeID` generator changed
1417When `nodeID` didn't define in broker options, the broker generated it from hostname (`os.hostname()`). It could cause problem for new users when they tried to start multiple instances on the same computer. Therefore, the broker generates `nodeID` from hostname **and process PID**. The newly generated nodeID looks like `server-6874` where `server` is the hostname and `6874` is the PID.
1418
1419## Protocol changed
1420The transport protocol is changed. The new version is `3`. [Check the changes.](https://github.com/moleculerjs/moleculer/blob/aa56e0072f4726dcd3a72ef164c3e13ad377bfc2/docs/PROTOCOL.md)
1421
1422**It means, the >=0.12.x versions can't communicate with old <=0.11 versions.**
1423
1424**Changes:**
1425- the `RESPONSE` packet has a new field `meta`.
1426- the `EVENT` packet has a new field `broadcast`.
1427- the `port` field is removed from `INFO` packet.
1428- the `INFO` packet has a new field `hostname`.
1429
1430# New features
1431
1432## New ServiceBroker options
1433There are some new properties in ServiceBroker option: `middlewares`, `created`, `started`, `stopped`.
1434
1435They can be useful when you use broker config file and start your project with Moleculer Runner.
1436
1437```js
1438// moleculer.config.js
1439module.exports = {
1440 logger: true,
1441
1442 // Add middlewares
1443 middlewares: [myMiddleware()],
1444
1445 // Fired when the broker created
1446 created(broker) {
1447 },
1448
1449 // Fired when the broker started
1450 started(broker) {
1451 // You can return Promise
1452 return broker.Promise.resolve();
1453 },
1454
1455 // Fired when the broker stopped
1456 stopped(broker) {
1457 // You can return Promise
1458 return broker.Promise.resolve();
1459 }
1460};
1461```
1462
1463## Broadcast events with group filter
1464
1465The `broker.broadcast` function has a third `groups` argument similar to `broker.emit`.
1466```js
1467// Send to all "mail" service instances
1468broker.broadcast("user.created", { user }, "mail");
1469
1470// Send to all "user" & "purchase" service instances.
1471broker.broadcast("user.created", { user }, ["user", "purchase"]);
1472```
1473
1474## CPU usage-based strategy
1475There is a new `CpuUsageStrategy` strategy. It selects a node which has the lowest CPU usage.
1476Due to the node list can be very long, it gets samples and selects the node with the lowest CPU usage from only samples instead of the whole node list.
1477
1478There are 2 options for the strategy:
1479- `sampleCount`: the number of samples. Default: `3`
1480- `lowCpuUsage`: the low CPU usage percent. The node which has lower CPU usage than this value is selected immediately. Default: `10`
1481
1482**Usage:**
1483```js
1484const broker = new ServiceBroker({
1485 registry: {
1486 strategy: "CpuUsage"
1487 }
1488});
1489```
1490
1491**Usage with custom options**
1492```js
1493const broker = new ServiceBroker({
1494 registry: {
1495 strategy: "CpuUsage",
1496 strategyOptions: {
1497 sampleCount: 3,
1498 lowCpuUsage: 10
1499 }
1500 }
1501});
1502```
1503
1504## Starting logic is changed
1505The broker & services starting logic has been changed.
1506
1507**Previous logic:** the broker starts transporter connecting. When it's done, it starts all services (calls service `started` handlers). It has a disadvantage because other nodes can send requests to these services, while they are still starting and not ready yet.
1508
1509**New logic:** the broker starts transporter connecting but it doesn't publish the local service list to remote nodes. When it's done, it starts all services (calls service `started` handlers). Once all services start successfully, broker publishes the local service list to remote nodes. Hence other nodes send requests only after all local service started properly.
1510>Please note: you can make dead-locks when two services wait for each other. E.g.: `users` service has `dependencies: [posts]` and `posts` service has `dependencies: [users]`. To avoid it remove the concerned service from `dependencies` and use `waitForServices` method out of `started` handler instead.
1511
1512## Metadata is sent back to requester
1513At requests, `ctx.meta` is sent back to the caller service. You can use it to send extra meta information back to the caller.
1514E.g.: send response headers back to API gateway or set resolved logged in user to metadata.
1515
1516**Export & download a file with API gateway:**
1517```js
1518// Export data
1519export(ctx) {
1520 const rows = this.adapter.find({});
1521
1522 // Set response headers to download it as a file
1523 ctx.meta.headers = {
1524 "Content-Type": "application/json; charset=utf-8",
1525 "Content-Disposition": 'attachment; filename=\"book.json\"'
1526 }
1527
1528 return rows;
1529}
1530```
1531
1532**Authenticate:**
1533```js
1534auth(ctx) {
1535 let user = this.getUserByJWT(ctx.params.token);
1536 if (ctx.meta.user) {
1537 ctx.meta.user = user;
1538
1539 return true;
1540 }
1541
1542 throw new Forbidden();
1543}
1544```
1545
1546## Better ES6 class support
1547If you like better ES6 classes than Moleculer service schema, you can write your services in ES6 classes.
1548
1549There are two ways to do it:
1550
15511. **Native ES6 classes with schema parsing**
1552
1553 Define `actions` and `events` handlers as class methods. Call the `parseServiceSchema` method in constructor with schema definition where the handlers pointed to these class methods.
1554 ```js
1555 const Service = require("moleculer").Service;
1556
1557 class GreeterService extends Service {
1558
1559 constructor(broker) {
1560 super(broker);
1561
1562 this.parseServiceSchema({
1563 name: "greeter",
1564 version: "v2",
1565 meta: {
1566 scalable: true
1567 },
1568 dependencies: [
1569 "auth",
1570 "users"
1571 ],
1572
1573 settings: {
1574 upperCase: true
1575 },
1576 actions: {
1577 hello: this.hello,
1578 welcome: {
1579 cache: {
1580 keys: ["name"]
1581 },
1582 params: {
1583 name: "string"
1584 },
1585 handler: this.welcome
1586 }
1587 },
1588 events: {
1589 "user.created": this.userCreated
1590 },
1591 created: this.serviceCreated,
1592 started: this.serviceStarted,
1593 stopped: this.serviceStopped,
1594 });
1595 }
1596
1597 // Action handler
1598 hello() {
1599 return "Hello Moleculer";
1600 }
1601
1602 // Action handler
1603 welcome(ctx) {
1604 return this.sayWelcome(ctx.params.name);
1605 }
1606
1607 // Private method
1608 sayWelcome(name) {
1609 this.logger.info("Say hello to", name);
1610 return `Welcome, ${this.settings.upperCase ? name.toUpperCase() : name}`;
1611 }
1612
1613 // Event handler
1614 userCreated(user) {
1615 this.broker.call("mail.send", { user });
1616 }
1617
1618 serviceCreated() {
1619 this.logger.info("ES6 Service created.");
1620 }
1621
1622 serviceStarted() {
1623 this.logger.info("ES6 Service started.");
1624 }
1625
1626 serviceStopped() {
1627 this.logger.info("ES6 Service stopped.");
1628 }
1629 }
1630
1631 module.exports = GreeterService;
1632 ```
1633
16342. **Use decorators**
1635
1636 Thanks for [@ColonelBundy](https://github.com/ColonelBundy), you can use ES7/TS decorators as well: [moleculer-decorators](https://github.com/ColonelBundy/moleculer-decorators)
1637
1638 >Please note, you need to use Typescript or Babel to compile decorators.
1639
1640 **Example service**
1641 ```js
1642 const moleculer = require('moleculer');
1643 const { Service, Action, Event, Method } = require('moleculer-decorators');
1644 const web = require('moleculer-web');
1645 const broker = new moleculer.ServiceBroker({
1646 logger: console,
1647 logLevel: "debug",
1648 });
1649
1650 @Service({
1651 mixins: [web],
1652 settings: {
1653 port: 3000,
1654 routes: [
1655 ...
1656 ]
1657 }
1658 })
1659 class ServiceName {
1660 @Action()
1661 Login(ctx) {
1662 ...
1663 }
1664
1665 // With options
1666 @Action({
1667 cache: false,
1668 params: {
1669 a: "number",
1670 b: "number"
1671 }
1672 })
1673 Login2(ctx) {
1674 ...
1675 }
1676
1677 @Event
1678 'event.name'(payload, sender, eventName) {
1679 ...
1680 }
1681
1682 @Method
1683 authorize(ctx, route, req, res) {
1684 ...
1685 }
1686
1687 hello() { // Private
1688 ...
1689 }
1690
1691 started() { // Reserved for moleculer, fired when started
1692 ...
1693 }
1694
1695 created() { // Reserved for moleculer, fired when created
1696 ...
1697 }
1698
1699 stopped() { // Reserved for moleculer, fired when stopped
1700 ...
1701 }
1702 }
1703
1704 broker.createService(ServiceName);
1705 broker.start();
1706 ```
1707
1708## Event group option
1709The broker groups the event listeners by group name. The group name is the name of the service where your event handler is declared. You can change it in the event definition.
1710
1711```js
1712module.export = {
1713 name: "payment",
1714 events: {
1715 "order.created": {
1716 // Register handler to "other" group instead of "payment" group.
1717 group: "other",
1718 handler(payload) {
1719 // ...
1720 }
1721 }
1722 }
1723}
1724```
1725
1726## New experimental TCP transporter with UDP discovery
1727There is a new built-in zero-config TCP transporter. It uses [Gossip protocol](https://en.wikipedia.org/wiki/Gossip_protocol) to disseminate node info, service list and heartbeats. It has an integrated UDP discovery to detect new nodes on the network. It uses multicast discovery messages.
1728If the UDP is prohibited on your network, you can use `urls` option. It is a list of remote endpoints (host/ip, port, nodeID). It can be a static list in your configuration or a file path which contains the list.
1729
1730>Please note, you don't need to list all remote nodes. It's enough at least one node which is online. For example, you can create a "serviceless" gossiper node, which does nothing, just shares remote nodes addresses by gossip messages. So all nodes need to know only the gossiper node address to be able to detect all other nodes.
1731
1732<!-- **This TCP transporter is the default transporter in Moleculer**.
1733It means, you don't have to configure any transporter, just start the brokers/nodes, use same namespaces and the nodes will find each others.
1734>If you don't want to use transporter, set `transporter: null` in broker options.
1735-->
1736
1737**Use TCP transporter with default options**
1738```js
1739const broker = new ServiceBroker({
1740 transporter: "TCP"
1741});
1742```
1743
1744**Use TCP transporter with static node list**
1745```js
1746const broker = new ServiceBroker({
1747 transporter: "tcp://172.17.0.1:6000/node-1,172.17.0.2:6000/node-2"
1748});
1749```
1750or
1751```js
1752const broker = new ServiceBroker({
1753 nodeID: "node-1",
1754 transporter: {
1755 type: "TCP",
1756 options: {
1757 udpDiscovery: false,
1758 urls: [
1759 "172.17.0.1:6000/node-1",
1760 "172.17.0.2:6000/node-2",
1761 "172.17.0.3:6000/node-3"
1762 ]
1763 }
1764 }
1765});
1766```
1767
1768
1769**All TCP transporter options with default values**
1770```js
1771const broker = new ServiceBroker({
1772 logger: true,
1773 transporter: {
1774 type: "TCP",
1775 options: {
1776 // Enable UDP discovery
1777 udpDiscovery: true,
1778 // Reusing UDP server socket
1779 udpReuseAddr: true,
1780
1781 // UDP port
1782 udpPort: 4445,
1783 // UDP bind address (if null, bind on all interfaces)
1784 udpBindAddress: null,
1785 // UDP sending period (seconds)
1786 udpPeriod: 30,
1787
1788 // Multicast address.
1789 udpMulticast: "239.0.0.0",
1790 // Multicast TTL setting
1791 udpMulticastTTL: 1,
1792
1793 // Send broadcast (Boolean, String, Array<String>)
1794 udpBroadcast: false,
1795
1796 // TCP server port. Null or 0 means random port
1797 port: null,
1798 // Static remote nodes address list (when UDP discovery is not available)
1799 urls: null,
1800 // Use hostname as preffered connection address
1801 useHostname: true,
1802
1803 // Gossip sending period in seconds
1804 gossipPeriod: 2,
1805 // Maximum enabled outgoing connections. If reach, close the old connections
1806 maxConnections: 32,
1807 // Maximum TCP packet size
1808 maxPacketSize: 1 * 1024 * 1024
1809 }
1810 }
1811});
1812```
1813
1814## New experimental transporter for Kafka
1815There is a new transporter for [Kafka](https://kafka.apache.org/). It is a very simple implementation. It transfers Moleculer packets to consumers via pub/sub. There are not implemented offset, replay...etc features.
1816Please note, it is an **experimental** transporter. **Do not use it in production yet!**
1817
1818>To use it, install `kafka-node` with `npm install kafka-node --save` command.
1819
1820**Connect to Zookeeper**
1821```js
1822const broker = new ServiceBroker({
1823 logger: true,
1824 transporter: "kafka://192.168.51.29:2181"
1825});
1826```
1827
1828**Connect to Zookeeper with custom options**
1829```js
1830const broker = new ServiceBroker({
1831 logger: true,
1832 transporter: {
1833 type: "kafka",
1834 options: {
1835 host: "192.168.51.29:2181",
1836
1837 // KafkaClient options. More info: https://github.com/SOHU-Co/kafka-node#clientconnectionstring-clientid-zkoptions-noackbatchoptions-ssloptions
1838 client: {
1839 zkOptions: undefined,
1840 noAckBatchOptions: undefined,
1841 sslOptions: undefined
1842 },
1843
1844 // KafkaProducer options. More info: https://github.com/SOHU-Co/kafka-node#producerclient-options-custompartitioner
1845 producer: {},
1846 customPartitioner: undefined,
1847
1848 // ConsumerGroup options. More info: https://github.com/SOHU-Co/kafka-node#consumergroupoptions-topics
1849 consumer: {
1850 },
1851
1852 // Advanced options for `send`. More info: https://github.com/SOHU-Co/kafka-node#sendpayloads-cb
1853 publish: {
1854 partition: 0,
1855 attributes: 0
1856 }
1857 }
1858 }
1859
1860});
1861```
1862
1863## New experimental transporter for NATS Streaming
1864There is a new transporter for [NATS Streaming](https://nats.io/documentation/streaming/nats-streaming-intro/). It is a very simple implementation. It transfers Moleculer packets to consumers via pub/sub. There are not implemented offset, replay...etc features.
1865Please note, it is an **experimental** transporter. **Do not use it in production yet!**
1866
1867>To use it, install `node-nats-streaming` with `npm install node-nats-streaming --save` command.
1868
1869**Connect to NATS Streaming server**
1870```js
1871// Shorthand to local server
1872const broker = new ServiceBroker({
1873 logger: true,
1874 transporter: "STAN"
1875});
1876
1877// Shorthand
1878const broker = new ServiceBroker({
1879 logger: true,
1880 transporter: "stan://192.168.0.120:4222"
1881});
1882
1883// Shorthand with options
1884const broker = new ServiceBroker({
1885 logger: true,
1886 transporter: {
1887 type: "STAN",
1888 options: {
1889 url: "stan://127.0.0.1:4222",
1890 clusterID: "my-cluster"
1891 }
1892 }
1893});
1894
1895```
1896
1897## Define custom REPL commands in broker options
1898You can define your custom REPL commands in broker options to extend Moleculer REPL commands.
1899
1900```js
1901const broker = new ServiceBroker({
1902 logger: true,
1903 replCommands: [
1904 {
1905 command: "hello <name>",
1906 description: "Call the greeter.hello service with name",
1907 alias: "hi",
1908 options: [
1909 { option: "-u, --uppercase", description: "Uppercase the name" }
1910 ],
1911 types: {
1912 string: ["name"],
1913 boolean: ["u", "uppercase"]
1914 },
1915 //parse(command, args) {},
1916 //validate(args) {},
1917 //help(args) {},
1918 allowUnknownOptions: true,
1919 action(broker, args) {
1920 const name = args.options.uppercase ? args.name.toUpperCase() : args.name;
1921 return broker.call("greeter.hello", { name }).then(console.log);
1922 }
1923 }
1924 ]
1925});
1926
1927broker.repl();
1928```
1929
1930# Changes
1931- MemoryCacher clears all cache entries after the transporter connected/reconnected.
1932- `broker.loadServices` file mask is changed from `*.service.js` to `**/*.service.js` in order to load all services from subfolders, too.
1933- `ServiceNotFoundError` and `ServiceNotAvailableError` errors are retryable errors.
1934- `Strategy.select` method gets only available endpoint list.
1935- old unavailable nodes are removed from registry after 10 minutes.
1936- CPU usage in `HEARTBEAT` packet is working properly in Windows, too.
1937- register middlewares before internal service (`$node.*`) loading.
1938- `broker.getAction` deprecated method is removed.
1939- `PROTOCOL_VERSION` constant is available via broker as `ServiceBroker.PROTOCOL_VERSION` or `broker.PROTOCOL_VERSION`
1940- serialization functions are moved from transit to transporter codebase.
1941- `ctx.broadcast` shortcut method is created to send broadcast events from action handler.
1942- `broker.started` property is created to indicate broker starting state.
1943
1944# Fixes
1945- handles invalid `dependencies` value in service schema [#164](https://github.com/moleculerjs/moleculer/pull/164)
1946- fix event emit error if payload is `null`,
1947
1948--------------------------------------------------
1949<a name="0.11.10"></a>
1950# [0.11.10](https://github.com/moleculerjs/moleculer/compare/v0.11.9...v0.11.10) (2018-01-19)
1951
1952# New
1953
1954## Built-in clustering in Moleculer Runner [#169](https://github.com/moleculerjs/moleculer/pull/169)
1955By [@tinchoz49](https://github.com/tinchoz49 ) Moleculer Runner has a new built-in clustering function. With it, you can start multiple instances from your broker.
1956
1957Example to start all services from the `services` folder in 4 instances.
1958```bash
1959$ moleculer-runner --instances 4 services
1960```
1961> Please note, the `nodeID` will be suffixed with the worker ID.
1962
1963
1964## Context meta & params in metrics events [#166](https://github.com/moleculerjs/moleculer/pull/166)
1965By [@dani8art](https://github.com/dani8art) you can set that the broker put some `ctx.meta` and `ctx.params` fields to the metrics events.
1966You can define it in the action definition:
1967
1968```js
1969module.exports = {
1970 name: "test",
1971 actions: {
1972 import: {
1973 cache: true,
1974 metrics: {
1975 // Disable to add `ctx.params` to metrics payload. Default: false
1976 params: false,
1977 // Enable to add `ctx.meta` to metrics payload. Default: true
1978 meta: true
1979 },
1980 handler(ctx) {
1981 // ...
1982 }
1983 }
1984 }
1985}
1986```
1987
1988If the value is `true`, it adds all fields. If `Array`, it adds the specified fields. If `Function`, it calls with `params` or `meta`and you need to return an `Object`.
1989
1990--------------------------------------------------
1991<a name="0.11.9"></a>
1992# [0.11.9](https://github.com/moleculerjs/moleculer/compare/v0.11.8...v0.11.9) (2018-01-08)
1993
1994# New
1995
1996## Strategy resolver
1997
1998ServiceBroker can resolve the `strategy` from a string.
1999```js
2000const broker = new ServiceBroker({
2001 registry: {
2002 strategy: "Random"
2003 // strategy: "RoundRobin"
2004 }
2005});
2006```
2007
2008You can set it via env variables as well, if you are using the Moleculer Runner:
2009```sh
2010$ REGISTRY_STRATEGY=random
2011```
2012
2013## Load env files in Moleculer Runner [#158](https://github.com/moleculerjs/moleculer/issues/158)
2014Moleculer runner can load `.env` file at starting. There are two new cli options to load env file:
2015
2016* `-e, --env` - Load envorinment variables from the '.env' file from the current folder.
2017* `-E, --envfile <filename>` - Load envorinment variables from the specified file.
2018
2019**Example**
2020```sh
2021# Load the default .env file from current directory
2022$ moleculer-runner --env
2023
2024# Load the specified .my-env file
2025$ moleculer-runner --envfile .my-env
2026```
2027
2028# Fixes
2029- fixed hot reloading after broken service files by @askuzminov ([#155](https://github.com/moleculerjs/moleculer/pull/155))
2030- allow fallbackResponse to be falsy values
2031
2032
2033--------------------------------------------------
2034<a name="0.11.8"></a>
2035# [0.11.8](https://github.com/moleculerjs/moleculer/compare/v0.11.7...v0.11.8) (2017-12-15)
2036
2037# Changes
2038- `d.ts` has been improved.
2039
2040--------------------------------------------------
2041<a name="0.11.7"></a>
2042# [0.11.7](https://github.com/moleculerjs/moleculer/compare/v0.11.6...v0.11.7) (2017-12-05)
2043
2044# Changes
2045- `d.ts` has been improved.
2046
2047--------------------------------------------------
2048<a name="0.11.6"></a>
2049# [0.11.6](https://github.com/moleculerjs/moleculer/compare/v0.11.5...v0.11.6) (2017-11-07)
2050
2051# New
2052
2053## New cacher features
2054In action cache keys you can use meta keys with `#` prefix.
2055```js
2056broker.createService({
2057 name: "posts",
2058 actions: {
2059 list: {
2060 cache: {
2061 // Cache key: "limit" & "offset" from ctx.params, "user.id" from ctx.meta
2062 keys: ["limit", "offset", "#user.id"],
2063 ttl: 5
2064 },
2065 handler(ctx) {...}
2066 }
2067 }
2068});
2069```
2070
2071You can override the cacher default TTL setting in action definition.
2072```js
2073const broker = new ServiceBroker({
2074 cacher: {
2075 type: "memory",
2076 options: {
2077 ttl: 30 // 30 seconds
2078 }
2079 }
2080});
2081
2082broker.createService({
2083 name: "posts",
2084 actions: {
2085 list: {
2086 cache: {
2087 // This cache entries will be expired after 5 seconds instead of 30.
2088 ttl: 5
2089 },
2090 handler(ctx) {...}
2091 }
2092 }
2093});
2094```
2095
2096You can change the built-in cacher keygen function to your own one.
2097```js
2098const broker = new ServiceBroker({
2099 cacher: {
2100 type: "memory",
2101 options: {
2102 keygen(name, params, meta, keys) {
2103 // Generate a cache key
2104 return ...;
2105 }
2106 }
2107 }
2108});
2109```
2110
2111# Others
2112- `d.ts` has been improved by [@rmccallum81](https://github.com/rmccallum81)
2113
2114--------------------------------------------------
2115<a name="0.11.5"></a>
2116# [0.11.5](https://github.com/moleculerjs/moleculer/compare/v0.11.4...v0.11.5) (2017-10-12)
2117
2118# Changes
2119- `strategy` option has been fixed in broker option [#121](https://github.com/moleculerjs/moleculer/pull/121)
2120
2121
2122--------------------------------------------------
2123<a name="0.11.4"></a>
2124# [0.11.4](https://github.com/moleculerjs/moleculer/compare/v0.11.3...v0.11.4) (2017-10-11)
2125
2126# Changes
2127- Moleculer Runner arguments have been fixed (`services` arg)
2128- update AMQP default queue options by @Nathan-Schwartz [#119](https://github.com/moleculerjs/moleculer/pull/119)
2129
2130
2131--------------------------------------------------
2132<a name="0.11.3"></a>
2133# [0.11.3](https://github.com/moleculerjs/moleculer/compare/v0.11.2...v0.11.3) (2017-10-10)
2134
2135# Changes
2136- The `ack` handling has been fixed in AMQP transporter.
2137- AMQP RCP integration tests are added.
2138
2139
2140--------------------------------------------------
2141<a name="0.11.2"></a>
2142# [0.11.2](https://github.com/moleculerjs/moleculer/compare/v0.11.1...v0.11.2) (2017-10-06)
2143
2144# New
2145
2146## Service dependencies [#102](https://github.com/moleculerjs/moleculer/issues/102)
2147The `Service` schema has a new `dependencies` property. The serice can wait for other dependening ones when it starts. This way you don't need to call `waitForServices` in `started` any longer.
2148
2149```js
2150module.exports = {
2151 name: "posts",
2152 settings: {
2153 $dependencyTimeout: 30000 // Default: 0 - no timeout
2154 },
2155 dependencies: [
2156 "likes", // shorthand w/o version
2157 { name: "users", version: 2 }, // with numeric version
2158 { name: "comments", version: "staging" } // with string version
2159 ],
2160 started() {
2161 this.logger.info("Service started after the dependent services available.");
2162 }
2163 ....
2164}
2165```
2166The `started` service handler is called once the `likes`, `users` and `comments` services are registered (on the local or remote nodes).
2167
2168## Pending request queue size limit [#111](https://github.com/moleculerjs/moleculer/issues/111)
2169The `ServiceBroker` has a new `maxQueueSize` option under `transit` key. The broker protects the process to avoid crash during a high load with it. The `maxQueueSize` default value is 50,000. If pending request queue size reaches it, broker rejects the request with a `QueueIsFull` (retryable) error.
2170
2171```js
2172const broker = new ServiceBroker({
2173 transporter: "NATS",
2174 transit: {
2175 maxQueueSize: 10 * 1000
2176 }
2177}
2178```
2179
2180# Changes
2181
2182## The `waitForServices` method supports service versions [#112](https://github.com/moleculerjs/moleculer/issues/112)
2183By [@imatefx](https://github.com/imatefx), the `waitForServices` broker & service methods support service versions. Use the following formats to define version in a dependency:
2184
2185```js
2186module.exports = {
2187 name: "test",
2188 dependencies: { name: "users", version: 2 }
2189};
2190```
2191
2192--------------------------------------------------
2193<a name="0.11.1"></a>
2194# [0.11.1](https://github.com/moleculerjs/moleculer/compare/v0.11.0...v0.11.1) (2017-09-27)
2195
2196# New
2197
2198## Service metadata [#91](https://github.com/moleculerjs/moleculer/issues/91)
2199The `Service` schema has a new `metadata` property. The Moleculer modules doesn't use it, so you can use it whatever you want.
2200
2201```js
2202broker.createService({
2203 name: "posts",
2204 settings: {},
2205 metadata: {
2206 scalable: true,
2207 priority: 5
2208 },
2209
2210 actions: { ... }
2211});
2212```
2213
2214> The `metadata` is transferred between nodes, you can access it via `$node.services`. Or inside service with `this.metadata` like settings.
2215
2216## NATS transporter supports to use the built-in balancer
2217The NATS transporter has been changed. It supports to use the NATS built-in balancer instead of Moleculer balancer. In this case every `call` & `emit` will be transferred through NATS message broker.
2218
2219```js
2220const broker = new ServiceBroker({
2221 transporter: "NATS",
2222 disableBalancer: true
2223});
2224```
2225
2226# Changes
2227- ping nodes with `broker.sendPing` instead of `broker.transit.sendPing`.
2228- `index.d.ts` updated to v0.11
2229- AMQP integration tests has been rewritten.
2230- process exit code changed from `2` to `1` in `broker.fatal`. Reason: `2` is reserved by Bash for builtin misuse. [More info](https://nodejs.org/dist/latest-v6.x/docs/api/process.html#process_exit_codes)
2231
2232--------------------------------------------------
2233<a name="0.11.0"></a>
2234# [0.11.0](https://github.com/moleculerjs/moleculer/compare/v0.10.0...v0.11.0) (2017-09-12)
2235
2236# Breaking changes
2237
2238## Protocol changed [#86](https://github.com/moleculerjs/moleculer/issues/86)
2239The Moleculer transportation protocol has been changed. It means, **the new (>= v0.11) versions can't communicate with the old (<= v0.10.x) ones.**
2240You can find more information about changes in [#86](https://github.com/moleculerjs/moleculer/issues/86) issue.
2241
2242## Balanced events
2243The whole event handling has been rewritten. By now Moleculer supports [event driven architecture](http://microservices.io/patterns/data/event-driven-architecture.html). It means that event emits are balanced like action calls are.
2244
2245For example, you have 2 main services: `users` & `payments`. Both subscribe to the `user.created` event. You start 3 instances from `users` service and 2 instances from `payments` service. If you emit the event with `broker.emit('user.created')`, broker groups & balances the event, so only one `users` and one `payments` service receive the event.
2246You can also send broadcast events with the `broker.broadcast('user.created')` command. This way every service instance on every node receives the event.
2247The `broker.broadcastLocal('user.created')` command sends events only to the local services.
2248
2249## Renamed & new internal events
2250Every internal event name starts with '$'. These events are not transferred to remote nodes.
2251
2252**Renamed events:**
2253- `node.connected` -> `$node.connected`
2254- `node.updated` -> `$node.updated`
2255- `node.disconnected` -> `$node.disconnected`
2256- `services.changed` -> `$services.changed`. It is called if local or remote service list is changed.
2257- `circuit-breaker.closed` -> `$circuit-breaker.closed`
2258- `circuit-breaker.opened` -> `$circuit-breaker.opened`
2259- `circuit-breaker.half-opened` -> `$circuit-breaker.half-opened`
2260
2261**New events:**
2262- global circuit breaker events for metrics: `metrics.circuit-breaker.closed`, `metrics.circuit-breaker.opened`, `metrics.circuit-breaker.half-opened`
2263
2264## Switchable built-in load balancer
2265The built-in Moleculer load balancer is switchable. You can turn it off, if the transporter has internal balancer (currently AMQP has it).
2266
2267```js
2268const broker = new ServiceBroker({
2269 disableBalancer: false
2270});
2271```
2272
2273> Please note! If built-in balancer is disabled, every call & emit (including local ones too) are transferred via transporter.
2274
2275## Removed broker methods
2276Some internal broker methods have been removed or renamed.
2277- `broker.bus` has been removed.
2278- `broker.on` has been removed. Use `events` in service schema instead.
2279- `broker.once` has been removed.
2280- `broker.off` has been removed.
2281- `broker.getService` has been renamed to `broker.getLocalService`
2282- `broker.hasService` has been removed.
2283- `broker.hasAction` has been removed.
2284- `broker.getAction` has been deprecated.
2285- `broker.isActionAvailable` has been removed.
2286
2287## Changed local service responses
2288Internal action (`$node.list`, `$node.services`, `$node.actions`, `$node.health`) responses are changed. New internal action (`$node.events`) to list event subscriptiion is added.
2289
2290## Broker option changes
2291- `heartbeatInterval` default value is changed from `10` to `5`.
2292- `heartbeatTimeout` default value is changed from `30` to `15`.
2293- `circuitBreaker.maxFailures` default value is changed from `5` to `3`.
2294- `logFormatter` accepts string. The `simple` value is a new formatter to show only log level & log messages.
2295
2296# New
2297
2298## Ping command
2299New PING & PONG feature has been implemented. Ping remite nodes to measure the network latency and system time differences.
2300
2301```js
2302broker.createService({
2303 name: "test",
2304 events: {
2305 "$node.pong"({ nodeID, elapsedTime, timeDiff }) {
2306 this.logger.info(`Pong received from '${nodeID}' - Time: ${elapsedTime}ms, System time difference: ${timeDiff}ms`);
2307 }
2308 }
2309});
2310
2311broker.start().then(() => broker.transit.sendPing(/*nodeID*/));
2312```
2313
2314## Pluggable validator
2315The Validator in ServiceBroker is plugable. So you can change the built-in `fastest-validator` to a slower one :) [Example Joi validator](https://gist.github.com/icebob/07024c0ac22589a5496473c2a8a91146)
2316
2317## Waiting for other services feature
2318If your services depend on other ones, use the `waitForService` method to make services wait until dependencies start.
2319
2320```js
2321let svc = broker.createService({
2322 name: "seed",
2323 started() {
2324 return this.waitForServices(["posts", "users"]).then(() => {
2325 // Do work...
2326 });
2327 }
2328});
2329```
2330
2331Signature:
2332```js
2333this.waitForServices(serviceNames: String|Array<String>, timeout: Number/*milliseconds*/, interval: Number/*milliseconds*/): Promise
2334```
2335
2336## New error types
2337We added some new Moleculer error classes.
2338- `MoleculerRetryableError` - Common Retryable error. Caller retries the request if `retryCount > 0`.
2339- `MoleculerServerError` - Common server error (5xx).
2340- `MoleculerClientError` - Common client/request error (4xx).
2341- `ServiceNotAvailable` - Raises if the service is registered but isn't available (no live nodes or CB disabled them).
2342- `ProtocolVersionMismatchError` - Raises if connect a node with an older client (<= v0.10.0)).
2343
2344# Other changes
2345- The cachers don't listen "cache.clean" event.
2346
2347--------------------------------------------------
2348<a name="0.10.0"></a>
2349# [0.10.0](https://github.com/moleculerjs/moleculer/compare/v0.9.0...v0.10.0) (2017-08-20)
2350
2351# Breaking changes
2352
2353## No more `nodeID == null` in local stuff
2354In all core modules removed the nullable `nodeID`. Every places (context, events, $node.* results) the nodeID contains a valid (local or remote) nodeID. On local nodes it equals with `broker.nodeID`.
2355
2356**Migration guide**
2357
2358Before:
2359```js
2360if (ctx.nodeID == null) { ... }
2361// ---------
2362events: {
2363 "users.created"(payload, sender) {
2364 if (sender == null) { ... }
2365 }
2366}
2367```
2368
2369After:
2370```js
2371if (ctx.nodeID == ctx.broker.nodeID) { ... }
2372// ---------
2373events: {
2374 "users.created"(payload, sender) {
2375 if (sender == this.broker.nodeID) { ... }
2376 }
2377}
2378```
2379
2380## `internalActions` is renamed to `internalServices`
2381The `internalActions` broker option is renamed to `internalServices`.
2382
2383## Removed `broker.createNewContext` method
2384The `createNewContext` broker method is moved to `Context`class as a static method.
2385
2386**Migration guide:**
2387
2388Before:
2389```js
2390let ctx = broker.createNewContext(action, nodeID, params, opts);
2391```
2392
2393After:
2394```js
2395let ctx = Context.create(broker, action, nodeID, params, opts);
2396// or better
2397let ctx = broker.ContextFactory.create(broker, action, nodeID, params, opts);
2398```
2399
2400## Removed `LOCAL_NODE_ID` constant
2401The recently added `LOCAL_NODE_ID` constant is removed. If you want to check the nodeID is local, please use the `if (nodeID == broker.nodeID)` syntax.
2402
2403## Class based pluggable Service registry strategies [#75](https://github.com/moleculerjs/moleculer/pull/75)
2404By @WoLfulus, the service registry balancer strategy is now pluggable.
2405
2406**New syntax:**
2407```js
2408let Strategies = require("moleculer").Strategies;
2409
2410const broker = new ServiceBroker({
2411 registry: {
2412 strategy: new Strategies.RoundRobin()
2413 }
2414});
2415```
2416
2417**Custom strategy**
2418
2419You can create you custom strategy.
2420
2421```js
2422let BaseStrategy = require("moleculer").Strategies.Base;
2423
2424class CustomStrategy extends BaseStrategy {
2425 select(list) {
2426 return list[0];
2427 }
2428};
2429
2430const broker = new ServiceBroker({
2431 registry: {
2432 strategy: new CustomStrategy()
2433 }
2434});
2435```
2436
2437## Metrics event payloads are changed
2438The metrics payload contains `remoteCall` and `callerNodeID` properties. The `remoteCall` is true if the request is called from a remote node. In this case the `callerNodeID` contains the caller nodeID.
2439
2440`metrics.trace.span.start`:
2441```js
2442{
2443 "action": {
2444 "name": "users.get"
2445 },
2446 "id": "123123123",
2447 "level": 1,
2448 "parent": 123,
2449 "remoteCall": true,
2450 "requestID": "abcdef",
2451 "startTime": 123456789,
2452 "nodeID": "node-1",
2453 "callerNodeID": "node-2"
2454}
2455```
2456
2457`metrics.trace.span.start`:
2458```js
2459{
2460 "action": {
2461 "name": "users.get"
2462 },
2463 "duration": 45,
2464 "id": "123123123",
2465 "parent": 123,
2466 "requestID": "abcdef",
2467 "startTime": 123456789,
2468 "endTime": 123456795,
2469 "fromCache": false,
2470 "level": 1,
2471 "remoteCall": true,
2472 "nodeID": "node-1",
2473 "callerNodeID": "node-2"
2474}
2475```
2476
2477# New
2478
2479## Hot reload services [#82](https://github.com/moleculerjs/moleculer/pull/82)
2480The ServiceBroker supports hot reloading services. If you enable it broker will watch file changes. If you modify service file, broker will reload it on-the-fly.
2481[Demo video](https://www.youtube.com/watch?v=l9FsAvje4F4)
2482
2483> Note: Hot reloading is only working with Moleculer Runner or if you load your services with `broker.loadService` or `broker.loadServices`.
2484
2485**Usage**
2486
2487```js
2488const broker = new ServiceBroker({
2489 logger: console,
2490 hotReload: true
2491});
2492
2493broker.loadService("./services/test.service.js");
2494```
2495
2496**Usage with Moleculer Runner**
2497
2498Turn it on with `--hot` or `-H` flags.
2499
2500```bash
2501$ moleculer-runner --hot ./services/test.service.js
2502```
2503
2504## Protocol documentation
2505Moleculer protocol documentation is available in [docs/PROTOCOL.md](docs/PROTOCOL.md) file.
2506
2507# AMQP transporter [#72](https://github.com/moleculerjs/moleculer/pull/72)
2508By @Nathan-Schwartz, AMQP (for RabbitMQ) transporter added to Moleculer project.
2509
2510```js
2511const broker = new ServiceBroker({
2512 transporter: "amqp://guest:guest@rabbitmq-server:5672"
2513});
2514
2515const broker = new ServiceBroker({
2516 transporter: new AmqpTransporter({
2517 amqp: {
2518 url: "amqp://guest:guest@localhost:5672",
2519 eventTimeToLive: 5000,
2520 prefetch: 1
2521 }
2522 });
2523});
2524```
2525
2526--------------------------------------------------
2527
2528<a name="0.9.0"></a>
2529# [0.9.0](https://github.com/moleculerjs/moleculer/compare/v0.8.5...v0.9.0) (2017-08-10)
2530
2531# Breaking changes
2532
2533## Namespace support, removed `prefix` options [#57](https://github.com/moleculerjs/moleculer/issues/57)
2534The broker has a new `namespace` option to segment your services. For example, you are running development & production services (or more production services) on the same transporter. If you are using different `namespace` you can avoid collisions between different environments.
2535
2536> You can reach it in your services as `this.broker.namespace`.
2537
2538Thereupon the `prefix` option in transporters & cachers is removed.
2539
2540**Example**
2541```js
2542const broker = new ServiceBroker({
2543 logger: console,
2544 namespace: "DEV",
2545 transporter: "NATS",
2546 cacher: "Redis"
2547});
2548```
2549In this case the transporter & cacher prefix will be `MOL-DEV`.
2550
2551
2552## Renamed internal service settings
2553The `useVersionPrefix` is renamed to `$noVersionPrefix`. The `serviceNamePrefix` is renamed to `$noServiceNamePrefix`. Both settings logical state is changed.
2554The `cache` setting is renamed to `$cache`.
2555
2556### Migration guide
2557
2558**Before**
2559```js
2560broker.createService({
2561 name: "test",
2562 settings: {
2563 useVersionPrefix: false,
2564 serviceNamePrefix: false,
2565 cache: true
2566 }
2567});
2568```
2569
2570**After**
2571```js
2572broker.createService({
2573 name: "test",
2574 settings: {
2575 $noVersionPrefix: true,
2576 $noServiceNamePrefix: true,
2577 $cache: true
2578 }
2579});
2580```
2581
2582## Changed versioned action names [#58](https://github.com/moleculerjs/moleculer/issues/58)
2583Based on [#58](https://github.com/moleculerjs/moleculer/issues/58) if service version is a `String`, the version in action names won't be prefixed with `v`, expect if it is a `Number`.
2584
2585**Example**
2586```js
2587broker.createService({
2588 name: "test",
2589 version: 3,
2590 actions: {
2591 hello(ctx) {}
2592 }
2593});
2594broker.call("v3.test.hello");
2595
2596broker.createService({
2597 name: "test",
2598 version: "staging",
2599 actions: {
2600 hello(ctx) {}
2601 }
2602});
2603broker.call("staging.test.hello");
2604```
2605
2606## Module log level configuration is removed
2607The module log level is not supported. The `logLevel` option can be only `String`. It is used if the logger is the `console`. **In case of external loggers you have to handle log levels.**
2608
2609# New
2610
2611## Better logging [#61](https://github.com/moleculerjs/moleculer/issues/61)
2612The whole Moleculer logger is rewritten. It supports better the external loggers. The built-in log message format is also changed.
2613
2614### Built-in `console` logger
2615```js
2616const broker = createBroker({
2617 logger: console,
2618 logLevel: "info"
2619});
2620```
2621New console output:
2622![image](https://user-images.githubusercontent.com/306521/29127309-011bd0e0-7d21-11e7-87e2-c2d83352a857.png)
2623
2624**With custom `logFormatter`**
2625```js
2626const broker = new ServiceBroker({
2627 logger: console,
2628 logFormatter(level, args, bindings) {
2629 return level.toUpperCase() + " " + bindings.nodeID + ": " + args.join(" ");
2630 }
2631});
2632broker.logger.warn("Warn message");
2633broker.logger.error("Error message");
2634```
2635Output:
2636```
2637WARN dev-pc: Warn message
2638ERROR dev-pc: Error message
2639```
2640
2641### External loggers
2642
2643**[Pino](http://getpino.io/)**
2644```js
2645const pino = require("pino")({ level: "info" });
2646const broker = new ServiceBroker({
2647 logger: bindings => pino.child(bindings)
2648});
2649```
2650
2651Sample output:
2652![image](https://user-images.githubusercontent.com/306521/29127258-e151e3bc-7d20-11e7-9995-025f53cf41ec.png)
2653
2654
2655**[Bunyan](https://github.com/trentm/node-bunyan)**
2656```js
2657const bunyan = require("bunyan");
2658const logger = bunyan.createLogger({ name: "moleculer", level: "info" });
2659const broker = new ServiceBroker({
2660 logger: bindings => logger.child(bindings)
2661});
2662```
2663
2664Sample output:
2665![image](https://user-images.githubusercontent.com/306521/29127286-f2203428-7d20-11e7-84f1-c81aeaaaef53.png)
2666
2667**[Winston](https://github.com/winstonjs/winston)**
2668```js
2669const broker = new ServiceBroker({
2670 logger: bindings => new winston.Logger({
2671 transports: [
2672 new (winston.transports.Console)({
2673 timestamp: true,
2674 colorize: true,
2675 prettyPrint: true
2676 })
2677 ]
2678 })
2679});
2680```
2681
2682**[Winston context](https://github.com/citrix-research/node-winston-context)**
2683```js
2684const WinstonContext = require("winston-context");
2685const winston = require("winston");
2686const broker = createBroker({
2687 logger: bindings => new WinstonContext(winston, "", bindings)
2688});
2689```
2690
2691> Please note! Some external loggers have not `trace` & `fatal` log methods (e.g.: winston). In this case you have to extend your logger.
2692```js
2693const WinstonContext = require("winston-context");
2694const winston = require("winston");
2695const { extend } = require("moleculer").Logger;
2696const broker = createBroker({
2697 logger: bindings => extend(new WinstonContext(winston, "", bindings))
2698});
2699```
2700
2701The `bindings` contains the following properties:
2702- `ns` - namespace
2703- `nodeID` - nodeID
2704- `mod` - type of core module: `broker`, `cacher`, `transit`, `transporter`
2705- `svc` - service name
2706- `ver` - service version
2707
2708**Please avoid to use these property names when you log an `Object`.** For example: the `broker.logger.error({ mod: "peanut" })` overrides the original `mod` value!
2709
2710
2711## Dynamic service load & destroy
2712Available to load & destroy services after the broker started. For example you can hot-reload your services in runtime. The remote nodes will be notified about changes and the broker will emit a `services.changed` event locally.
2713
2714**Example**
2715```js
2716
2717broker.start().then(() => {
2718
2719 setTimeout(() => {
2720 // Create a new service after 5s
2721 broker.createService({
2722 name: "math",
2723 actions: {
2724 add(ctx) {
2725 return Number(ctx.params.a) + Number(ctx.params.b);
2726 },
2727 }
2728 });
2729
2730 }, 5000);
2731
2732 setTimeout(() => {
2733 // Destroy a created service after 10s
2734 let svc = broker.getService("math");
2735 broker.destroyService(svc);
2736
2737 }, 10000);
2738
2739});
2740```
2741
2742## Multiple service calls [#31](https://github.com/moleculerjs/moleculer/issues/31)
2743With `broker.mcall` method you can call multiple actions (in parallel).
2744
2745**Example with `Array`**
2746```js
2747broker.mcall([
2748 { action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
2749 { action: "users.find", params: {limit: 5, sort: "username"} }
2750]).then(results => {
2751 let posts = results[0];
2752 let users = results[1];
2753})
2754```
2755
2756**Example with `Object`**
2757```js
2758broker.mcall({
2759 posts: { action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
2760 users: { action: "users.find", params: {limit: 5, sort: "username"} }
2761}).then(results => {
2762 let posts = results.posts;
2763 let users = results.users;
2764})
2765```
2766
2767# Fixes
2768
2769--------------------------------------------------
2770<a name="0.8.5"></a>
2771# [0.8.5](https://github.com/moleculerjs/moleculer/compare/v0.8.4...v0.8.5) (2017-08-06)
2772
2773# Fixes
2774- fixed logger method bindings.
2775- fixed transporter shutdown errors [#62](https://github.com/moleculerjs/moleculer/issues/62)
2776
2777--------------------------------------------------
2778<a name="0.8.4"></a>
2779# [0.8.4](https://github.com/moleculerjs/moleculer/compare/v0.8.3...v0.8.4) (2017-07-24)
2780
2781# Fixes
2782- fixed `Calling error! TypeError : Cannot read property 'requestID' of undefined` error when you call a local action from other one directly.
2783
2784--------------------------------------------------
2785<a name="0.8.3"></a>
2786# [0.8.3](https://github.com/moleculerjs/moleculer/compare/v0.8.2...v0.8.3) (2017-07-24)
2787
2788# New
2789
2790## Removable actions in mixins
2791You can remove an existing action when mixing a service.
2792
2793```
2794broker.createService({
2795 name: "test",
2796 mixins: [OtherService],
2797 actions: {
2798 dangerAction: false
2799 }
2800});
2801```
2802In the `test` service the `dangerAction` action won't be registered.
2803
2804## Support NPM modules in `moleculer-runner`
2805You can load services from NPM module in `moleculer-runner`.
2806
2807**With CLI arguments**
2808```bash
2809$ moleculer-runner -r npm:moleculer-fake npm:moleculer-twilio
2810```
2811
2812**With env**
2813```bash
2814$ SERVICES=posts,users,npm:moleculer-fale,npm:moleculer-twilio
2815
2816$ moleculer-runner
2817```
2818
2819--------------------------------------------------
2820<a name="0.8.2"></a>
2821# [0.8.2](https://github.com/moleculerjs/moleculer/compare/v0.8.1...v0.8.2) (2017-07-06)
2822
2823# Fixes
2824- fixed Redis cacher option resolver in ServiceBroker. Now it accepts connection string.
2825
2826 ```js
2827 const broker = new ServiceBroker({
2828 cacher: "redis://localhost"
2829 });
2830 ```
2831
2832# New
2833
2834## Validator updated
2835The fastest-validator is updated to [v0.5.0](https://github.com/icebob/fastest-validator/releases/tag/v0.5.0). It supports multi rules & custom validators.
2836
2837--------------------------------------------------
2838<a name="0.8.1"></a>
2839# [0.8.1](https://github.com/moleculerjs/moleculer/compare/v0.8.0...v0.8.1) (2017-07-03)
2840
2841# New
2842
2843## Improved mixin's merge logic [#50](https://github.com/moleculerjs/moleculer/issues/50/)
2844The mixins merge logic is handle better events & lifecycle events. If you have a `created`, `started`, `stopped` lifecycle event or any other service event handler in your services, but your mixin has the same event, Moleculer will call all of them in your service and in mixins.
2845
2846[Read more about mixins](http://moleculer.services/docs/service.html#Mixins)
2847
2848--------------------------------------------------
2849
2850<a name="0.8.0"></a>
2851# [0.8.0](https://github.com/moleculerjs/moleculer/compare/v0.7.0...v0.8.0) (2017-06-21)
2852
2853# New
2854
2855## Project runner script
2856There is a new Moleculer project runner script in the `bin` folder.
2857You can use it if you want to create small repos for services. In this case you needn't to create a ServiceBroker with options. Just create a `moleculer.config.js` or `moleculer.config.json` file in the root of repo fill it with your options and call the `moleculer-runner` within the NPM scripts.
2858As an other solution you can put it to the environment variables instead of putting options to file.
2859
2860[Read more about runner](http://moleculer.services/docs/runner.html)
2861
2862## Shorthand for transporters, cachers and serializers in broker options
2863Some new resolvers are implemented in broker options to support shorthand configurations. This feature is enabled to load broker options easily from a JSON file or load from environment variables.
2864
2865**Usage for transporters**
2866```js
2867// Connect to the NATS default (localhost) server
2868const broker = new ServiceBroker({
2869 transporter: "NATS"
2870});
2871
2872// Connect to a NATS server with connection string
2873const broker = new ServiceBroker({
2874 transporter: "nats://nats-server:4222"
2875});
2876
2877// Connect to a NATS server with transporter options
2878const broker = new ServiceBroker({
2879 transporter: {
2880 type: "NATS",
2881 options: {
2882 prefix: "TEST",
2883 nats: {
2884 host: "nats-server",
2885 user: "admin",
2886 pass: "nats-pass"
2887 }
2888 }
2889 }
2890});
2891```
2892
2893**Usage for cachers**
2894```js
2895// Use a memory cacher
2896const broker = new ServiceBroker({
2897 cacher: true
2898 // or
2899 // cacher: "Memory"
2900});
2901
2902// Use a Redis cacher with default options
2903const broker = new ServiceBroker({
2904 cacher: "Redis"
2905});
2906
2907// Use a Redis cacher with options
2908const broker = new ServiceBroker({
2909 cacher: {
2910 type: "Redis",
2911 options: {
2912 ttl: 100
2913 }
2914 }
2915});
2916```
2917
2918**Usage for serializers**
2919```js
2920// Use the Avro serializer
2921const broker = new ServiceBroker({
2922 serializers: "Avro"
2923});
2924
2925// Use the Protocol Buffer serializer
2926const broker = new ServiceBroker({
2927 serializers: {
2928 type: "ProtoBuf"
2929 }
2930});
2931```
2932
2933## Built-in circuit breaker [#22](https://github.com/moleculerjs/moleculer/issues/22/)
2934A better circuit breaker solution has recently been implemented. As a result of this improvement every call (local and remote) is protected by the built-in circuit breaker.
2935You only need to enable it in broker options.
2936
2937**Usage**
2938```js
2939const broker = new ServiceBroker({
2940 circuitBreaker: {
2941 enabled: true, // Enable this feature
2942 maxFailures: 5, // Trip breaker on 5 failures
2943 halfOpenTime: 10 * 1000 // 10 sec to switch to `half-open` state
2944 failureOnTimeout: true // Failure if request timed out
2945 failureOnReject: true // Failure if request rejected with error code >= 500
2946 }
2947});
2948```
2949
2950*`nodeUnavailable` method is dropped.*
2951
2952## Service Registry module
2953A built-in Service Registry module was created. It handles actions of services on nodes, circuit breaker logic...etc. It would be pluggable in the future.
2954
2955You can change the load balancing strategies of Service Registry via broker options.
2956
2957**Example**
2958
2959```js
2960const { STRATEGY_ROUND_ROBIN, STRATEGY_RANDOM } = require("moleculer");
2961
2962const broker = new ServiceBroker({
2963 registry: {
2964 strategy: STRATEGY_ROUND_ROBIN, // Load balancing strategy
2965 preferLocal: true // First call local service if available
2966 }
2967});
2968```
2969
2970## REPL mode [#30](https://github.com/moleculerjs/moleculer/issues/30/)
2971Broker module has an interactive REPL mode. You can call actions, load services, also emit events, subscribe to & unsubscribe from events from your console. You can list registered nodes & actions.
2972> To use REPL mode please install the [moleculer-repl](https://github.com/moleculerjs/moleculer-repl) module with `npm install moleculer-repl --save` command.
2973
2974**Start REPL mode**
2975```js
2976const broker = new ServiceBroker({ logger: console });
2977
2978// Start REPL
2979broker.repl();
2980```
2981
2982**Commands**
2983```
2984 Commands:
2985
2986 help [command...] Provides help for a given command.
2987 exit Exits application.
2988 q Exit application
2989 call <actionName> [params] Call an action
2990 dcall <nodeID> <actionName> [params] Call a direct action
2991 emit <eventName> [payload] Emit an event
2992 load <servicePath> Load a service from file
2993 loadFolder <serviceFolder> [fileMask] Load all service from folder
2994 subscribe <eventName> Subscribe to an event
2995 unsubscribe <eventName> Unsubscribe from an event
2996 actions [options] List of actions
2997 nodes List of nodes
2998 info Information from broker
2999```
3000
3001### REPL Commands
3002
3003**List nodes**
3004```
3005mol $ nodes
3006```
3007![image](https://cloud.githubusercontent.com/assets/306521/26260893/67a579d4-3ccf-11e7-955a-70f252aa260d.png)
3008
3009**List services**
3010```
3011mol $ services
3012```
3013
3014**List actions**
3015```
3016mol $ actions
3017```
3018![image](https://cloud.githubusercontent.com/assets/306521/26260954/8ef9d44e-3ccf-11e7-995a-ccbe035b2a9a.png)
3019
3020**Show common informations**
3021```
3022mol $ info
3023```
3024![image](https://cloud.githubusercontent.com/assets/306521/26260974/aaea9b02-3ccf-11e7-9e1c-ec9150518791.png)
3025
3026**Call an action**
3027```
3028mol $ call "test.hello"
3029```
3030
3031**Call an action with params**
3032```
3033mol $ call "math.add" '{"a": 5, "b": 4}'
3034```
3035
3036**Direct call**
3037```
3038mol $ dcall server-2 "$node.health"
3039```
3040
3041**Emit an event**
3042```
3043mol $ emit "user.created"
3044```
3045
3046**Subscribe to an event**
3047```
3048mol $ subscribe "user.created"
3049```
3050
3051**Unsubscribe from an event**
3052```
3053mol $ unsubscribe "user.created"
3054```
3055
3056**Load a service**
3057```
3058mol $ load "./math.service.js"
3059```
3060
3061**Load services from folder**
3062```
3063mol $ load "./services"
3064```
3065
3066## Direct call
3067It is available to call an action directly on a specified node. To use it set `nodeID` in options of call.
3068
3069**Example**
3070
3071```js
3072broker.call("user.create", {}, { timeout: 5000, nodeID: "server-12" });
3073```
3074
3075## Mergeable schemas in `createService`
3076Now there is a second parameter of `broker.createService`. With it you can override the schema properties. You can use it to use a built-in service & override some props.
3077
3078**Example**
3079
3080```js
3081broker.createService(apiGwService, {
3082 settings: {
3083 // Change port setting
3084 port: 8080
3085 },
3086 actions: {
3087 myAction() {
3088 // Add a new action to apiGwService service
3089 }
3090 },
3091
3092 created() {
3093 // Overwrite apiGwService.created handler
3094 }
3095});
3096```
3097
3098Or you can merge it manually with `mergeSchemas` method.
3099```js
3100let mergedSchema = broker.mergeSchemas(origSchema, modifications);
3101broker.createService(mergedSchema);
3102```
3103
3104## Service mixins
3105Like mergeable schemas, the service may include any mixin schemas. The constructor of Service merges these mixins with the schema of Service. It is to reuse an other Service in your service or extend an other Service.
3106
3107**Examples**
3108
3109```js
3110const ApiGwService = require("moleculer-web");
3111
3112module.exports = {
3113 name: "api",
3114 mixins: [ApiGwService]
3115 settings: {
3116 // Change port setting
3117 port: 8080
3118 },
3119 actions: {
3120 myAction() {
3121 // Add a new action to apiGwService service
3122 }
3123 }
3124}
3125```
3126
3127## New option to protect calling loop
3128You can protect your app against calling loop with the new `maxCallLevel` option. If the `ctx.level` value reaches this limit, it throwns a `MaxCallLevelError` error.
3129
3130```js
3131const broker = new ServiceBroker({
3132 maxCallLevel: 100
3133});
3134```
3135
3136## New Service setting
3137There is a new `useVersionPrefix` option in Service settings. If it is `false`, Moleculer can't use the version number of service as prefix for action names. The name of service will be `users.find` instead of `v2.users.find`. The default is `true`.
3138
3139# Changes
3140
3141## Removed the `node.reconnected` and `node.broken` events (breaking)
3142We merged the `node.connected` and `node.reconnected` events. The payload is changed:
3143```js
3144{
3145 node: {...},
3146 reconnected: false // it indicates the node is connected or reconnected
3147}
3148```
3149
3150We merged also the `node.disconnected` and `node.broken` events. The payload is changed:
3151```js
3152{
3153 node: {...},
3154 unexpected: true // True: broken, not coming heart-beat, False: received "DISCONNECT" packet
3155}
3156```
3157
3158## Remove Transporter, Cacher and Serializers dependencies (breaking)
3159Moleculer doesn't contain dependencies for NATS, Redis, MQTT, MsgPack, Avro and Protobuf. So it need install manually in your project.
3160If you want to create a Moleculer project which communicates via NATS and your Redis cacher, you have to install `npm install moleculer nats redis --save`
3161
3162## Changed code of ServiceNotFoundError
3163The code of `ServiceNotFoundError` is changed from `501` to `404`. [More info](https://github.com/moleculerjs/moleculer-web/issues/7)
3164
3165## Using Nanomatch instead of micromatch
3166Memory cacher is using [nanomatch](https://github.com/micromatch/nanomatch) instead of [micromatch](https://github.com/micromatch/micromatch). The `nanomatch` is ~10x faster.
3167
3168## Removed `metricsSendInterval` option [#24](https://github.com/moleculerjs/moleculer/issues/24/)
3169The `metricsSendInterval` option is removed from broker options. If you want to access statistics & health info, call the `$node.health` and `$node.stats` actions.
3170
3171## Metrics & Statistics separated [#24](https://github.com/moleculerjs/moleculer/issues/24/)
3172The metrics & statistics features separated. You can use just metrics or just statistics.
3173
3174## Metrics nodeID
3175Metrics events contains two nodeID properties.
3176- `nodeID`: the "caller" nodeID
3177- `targetNodeID`: in case of remote call this is the remote nodeID
3178
3179## Response error with stack trace
3180If an action responses an error on a remote node, the transporter will send back the error to the caller with the stack traces.
3181```js
3182// It will print the original error stack trace.
3183broker.call("account.deposit").catch(err => console.log(err.stack));
3184```
3185
3186## Type property in custom error
3187The `CustomError` class renamed to `MoleculerError`. also it has a `type` new property. You can store here a custom error type. For example, if you have a `ValidationError`, in some cases the `name` & `code` is not enough. By `type` error causes are to be stored.
3188
3189**Example**
3190```js
3191const ERR_MISSING_ID = "ERR_MISSING_ID";
3192const ERR_ENTITY_NOT_FOUND = "ERR_ENTITY_NOT_FOUND";
3193
3194broker.createService({
3195 actions: {
3196 get(ctx) {
3197 if (ctx.params.id) {
3198 const entity = this.searchEntity(ctx.params.id);
3199 if (entity)
3200 return entity;
3201 else
3202 return Promise.reject(new ValidationError("Not found entity!", ERR_ENTITY_NOT_FOUND));
3203 } else
3204 return Promise.reject(new ValidationError("Please set the ID field!", ERR_MISSING_ID));
3205 }
3206 }
3207});
3208```
3209
3210## Renamed `appendServiceName` settings to `serviceNamePrefix` in Service schema
3211
3212## Fatal crash
3213The `ServiceBroker` has a new `fatal` method. If you call it, broker will log the message with `fatal` level and exit the process with code `2`.
3214
3215`broker.fatal(message, err, needExit = true)`
3216> If you are running your app in containers and it has restart policy, you can use it to restart your app.
3217
3218**Usage**
3219```js
3220
3221try {
3222 // Do something dangerous
3223} catch(err) {
3224 broker.fatal("Dangerous thing is happened!", err, true);
3225}
3226
3227```
3228
3229## Low-level changes
3230- new output of `$node.actions` and `$node.services`
3231- In packet `INFO` & `DISCOVER` changed the `actions` property to `services` and now it contains all services with actions of node
3232- splitted `broker.registerService` to `registerLocalService` and `registerRemoteService`
3233- new `broker.unregisterServicesByNode`. It will be called when a node disconnected
3234
3235--------------------------------------------------
3236
3237
3238<a name="0.7.0"></a>
3239# 0.7.0 (2017-04-24)
3240
3241# New
3242## Serializers for transporters [#10](https://github.com/moleculerjs/moleculer/issues/10/)
3243Implemented pluggable serializers.
3244Built-in serializers:
3245- [x] JSON (default)
3246- [x] [Avro](https://github.com/mtth/avsc)
3247- [x] [MsgPack](https://github.com/mcollina/msgpack5)
3248- [x] [ProtoBuf](https://developers.google.com/protocol-buffers/)
3249
3250**Usage**
3251```js
3252let JSONSerializer = require("moleculer").Serializers.JSON;
3253
3254const broker = new ServiceBroker({
3255 serializer: new JSONSerializer(),
3256 transporter: new Transporter(),
3257 nodeID: "node-1"
3258});
3259```
3260
3261## Typescript definition file [#5](https://github.com/moleculerjs/moleculer/issues/5)
3262Created an index.d.ts file. I'm not familiar in Typescript, so if you found error please help me and open a PR with fix. Thank you!
3263
3264## Metrics rate option
3265Added `metricsRate` options to broker. This property sets the rate of sampled calls.
3266- `1` means to metric all calls
3267- `0.5` means to metric 50% of calls
3268- `0.1` means to metric 10% of calls
3269
3270**Usage**
3271```js
3272const broker = new ServiceBroker({
3273 metrics: true,
3274 metricsRate: 0.1
3275});
3276```
3277
3278## Context meta data ([#16](https://github.com/moleculerjs/moleculer/pull/16))
3279Added `meta` prop to `Context`. The `meta` will be merged if has parent context.
3280In case of remote calls the metadata will be transfered to the target service.
3281
3282**Usage**
3283
3284Set meta in `broker.call`:
3285```js
3286// Broker call with meta data
3287broker.call("user.create", { name: "Adam", status: true}, {
3288 timeout: 1000,
3289 meta: {
3290 // Send logged in user data with request to the service
3291 loggedInUser: {
3292 userID: 45,
3293 roles: [ "admin" ]
3294 }
3295 }
3296})
3297```
3298
3299Access meta in action:
3300```js
3301broker.createService({
3302 name: "user",
3303 actions: {
3304 create(ctx) {
3305 const meta = ctx.meta;
3306 if (meta.loggedInUser && meta.loggedInUser.roles.indexOf("admin") !== -1)
3307 return Promise.resolve(...);
3308 else
3309 throw new MoleculerError("Access denied!");
3310 }
3311 }
3312});
3313```
3314
3315# Changes
3316
3317## Update benchmarkify
3318Benchmarkify updated & created continuous benchmarking with [bench-bot](https://github.com/icebob/bench-bot).
3319Bench-bot is a benchmark runner. If a new Pull Request opened, bench-bot will run benchmarks against the `master` branch and it will post the results to the PR conversation.
3320
3321## Timeout & fallback response handling in local calls too
3322- Can be use timeout & fallback response in local calls.
3323- Timeout handling move from `Transit` to `ServiceBroker`
3324- Remove `wrapContentAction`
3325- In case of calling error, Node will be unavailable only if the error code >= `500`
3326
3327## Context changes
3328- Removed `createSubContext`
3329- Removed `ctx.parent` and added `ctx.parentID`
3330- Removed options in constructor. New constructor syntax:
3331 ```js
3332 let ctx = new Context(broker, action);
3333 ctx.setParams({ a: 5 });
3334 ctx.generateID(); // for metrics
3335 ctx.requestID = requestID;
3336 ```
3337- Add Context reference to returned Promise
3338 ```js
3339 const p = broker.call("user.create");
3340 console.log("Context:", p.ctx);
3341 ```
3342
3343## Sender in event handlers
3344If an event triggered remotely on an other node, broker passes the nodeID of sender to the event handler as 2nd parameter.
3345```js
3346// Usage in subscription
3347broker.on("**", (payload, sender) => console.log(`Event from ${sender || "local"}:`, payload));
3348
3349// Usage in Service schema
3350broker.createService({
3351 ...
3352 events: {
3353 something(payload, sender) {
3354 console.log(`Something happened on '${sender}':`, payload);
3355 }
3356 }
3357});
3358```
3359
3360## Distributed timeout handling
3361Moleculer uses [distributed timeouts](https://www.datawire.io/guide/traffic/deadlines-distributed-timeouts-microservices/).In the chained calls the `ctx.call` decrement the original timeout value with the elapsed time. If the new calculated timeout is less or equal than 0, it'll skip the next calls because the first call is rejected with `RequestTimeoutError` error.
3362
3363
3364--------------------------------------------------
3365
3366
3367<a name="0.6.0"></a>
3368# 0.6.0 (2017-03-31)
3369
3370# New
3371
3372## Validator library changed
3373The previous `validatorjs` validator removed and added own very fast [fastest-validator](https://github.com/icebob/fastest-validator) library. It can 3M validations/sec. Hereafter validation is not the bottle-neck. Only -7% slower with validation.
3374
3375**Here is the new benchmark result:**
3376```
3377Suite: Call with param validator
3378√ No validator x 588,463 ops/sec ±1.11% (84 runs sampled)
3379√ With validator passes x 541,903 ops/sec ±1.41% (84 runs sampled)
3380√ With validator fail x 25,648 ops/sec ±1.62% (85 runs sampled)
3381 No validator 0.00% (588,463 ops/sec)
3382 With validator passes -7.91% (541,903 ops/sec)
3383 With validator fail -95.64% (25,648 ops/sec)
3384```
3385
3386**Example params definition:**
3387```js
3388mult: {
3389 params: {
3390 a: { type: "number" },
3391 b: { type: "number" }
3392 },
3393 handler(ctx) {
3394 return Number(ctx.params.a) * Number(ctx.params.b);
3395 }
3396}
3397```
3398
3399**Validation error object:**
3400```js
3401[ {
3402 type: 'number',
3403 field: 'b',
3404 message: 'The \'b\' field must be a number!'
3405} ]
3406```
3407
3408# Changes
3409## Added & removed log levels
3410* Added 2 new log levels (`fatal` and `trace`);
3411* Removed unused `log` level. Use `info` level instead.
3412
3413**Available levels:**
3414```js
3415logger.trace("trace level");
3416logger.debug("debug level");
3417logger.info("info level");
3418logger.warn("warn level");
3419logger.error("error level");
3420logger.fatal("fatal level");
3421```
3422
3423**Logger fallback levels:**
3424* `trace` -> `debug` -> `info`
3425* `debug` -> `info`
3426* `info`: main level, no fallback
3427* `warn` -> `error` -> `info`
3428* `error` -> `info`
3429* `fatal` -> `error` -> `info`
3430
3431
3432--------------------------------------------------
3433
3434
3435<a name="0.5.0"></a>
3436# 0.5.0 (2017-02-26)
3437
3438First release.