Actions sind die aufrufbaren/öffentlichen Methoden des Services. Der Action-Aufruf repräsentiert einen Remote-Procedure-Call (RPC). Eine Action hat Request-Parameter & gibt, ähnlich wie ein HTTP-Request, einen Response zurück.
Wenn es mehrere Instanzen desselben Services gibt, verteilt der Broker eingehende Requests via Load-Balancing an die Instanzen. Mehr zum Thema Load-Balancing.
Service Aufrufe
Um einen Aufruf an einen Service zu senden nutze die broker.call Methode. Der Broker sucht nach einem Service (und einem Node) mit der entsprechenden Action und ruft diese auf. Die Funktion gibt einen Promise zurück.
Syntax
const res = warte broker.call(actionName, params, opts); |
Der actionName ist ein mit Punkt getrennter String. Der erste Teil ist der Name des Services, während der zweite Teil den Namen der Action darstellt. Wenn es also einen posts Service mit einer create Action gibt, kann man diese mit posts.create aufrufen.
Das params ist ein Objekt welches von der Action als Teil des Contexts mitgegeben wird. Der Service kann mit ctx.params darauf zugreifen. Es ist optional. Wenn es nicht definiert wird, ist es {}.
Das opts ist ein Objekt um einige Request-Parameter zu setzen/überschreiben wie z. B. timeout oder retryCount. Es ist optional.
Verfügbare Aufruf-Optionen:
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
timeout |
Number |
null |
Timeout des Requests in Millisekunden. Wenn der Request in einen Timeout läuft und kein fallbackResponse definiert wurde, wirft der broker einen RequestTimeout Fehler. Zum Deaktivieren muss der Wert auf 0 gesetzt werden. If it’s not defined, the requestTimeout value from broker options will be used. Read more. |
retries |
Number |
null |
Count of retry of request. If the request is timed out, broker will try to call again. Zum Deaktivieren muss der Wert auf 0 gesetzt werden. If it’s not defined, the retryPolicy.retries value from broker options will be used. Read more. |
fallbackResponse |
Any |
null |
Returns it, if the request has failed. Read more. |
nodeID |
String |
null |
Target nodeID. If set, it will make a direct call to the specified node. |
meta |
Object |
{} |
Metadata of request. Access it via ctx.meta in actions handlers. It will be transferred & merged at nested calls, as well. |
parentCtx |
Context |
null |
Parent Context instance. Use it to chain the calls. |
requestID |
String |
null |
Request ID or Correlation ID. Use it for tracing. |
Usages
Call without params
const res = await broker.call("user.list"); |
Call with params
const res = await broker.call("user.get", { id: 3 }); |
Call with calling options
const res = await broker.call("user.recommendation", { limit: 5 }, { |
Call with promise error handling
broker.call("posts.update", { id: 2, title: "Modified post title" }) |
Direct call: get health info from the “node-21” node
const res = await broker.call("$node.health", null, { nodeID: "node-21" }) |
Metadata
Send meta information to services with meta property. Access it via ctx.meta in action handlers. Please note that in nested calls the meta is merged.
broker.createService({ |
The meta is sent back to the caller service. Use it to send extra meta information back to the caller. E.g.: send response headers back to API gateway or set resolved logged in user to metadata.
broker.createService({ |
When making internal calls to actions (this.actions.xy()) you should set parentCtx to pass meta data.
Internal calls
broker.createService({ |
Timeout
Timeout can be set in action definition, as well. It overwrites the global broker requestTimeout option, but not the timeout in calling options.
Example
// moleculer.config.js
module.exports = {
nodeID: "node-1",
requestTimeout: 3000
};
// greeter.service.js
module.exports = {
name: "greeter",
actions: {
normal: {
handler(ctx) {
return "Normal";
}
},
slow: {
timeout: 5000, // 5 secs
handler(ctx) {
return "Slow";
}
}
},
Calling examples
// It uses the global 3000 timeout |
Multiple calls
Calling multiple actions at the same time is also possible. To do it use broker.mcall or ctx.mcall.mcall with Array <Object\>
await broker.mcall( |
mcall with Object and options.meta
await broker.mcall( |
settled option in broker.mcall
The mcall method has a new settled option to receive all Promise results. If settled: true, the mcall returns a resolved Promise in any case and the response contains the statuses and responses of all calls. Note that, without this option you won’t know how many (and which) calls were rejected.
Example
const res = await broker.mcall([ |
The res will be something similar to
[ |
Streaming
Moleculer supports Node.js streams as request params and as response. Use it to transfer an incoming file from a gateway, encode/decode or compress/decompress streams.
Examples
Send a file to a service as a stream
const stream = fs.createReadStream(fileName); |
Object Mode StreamingObject Mode Streaming is also supported. In order to enable it set
$streamObjectMode: trueinmeta.
Please note, the params should be a stream, you cannot add any additional variables to the params. Use the meta property to transfer additional data.
Receiving a stream in a service
module.exports = { |
Return a stream as response in a service
module.exports = { |
Process received stream on the caller side
const filename = "avatar-123.jpg"; |
AES encode/decode example service
const crypto = require("crypto"); |
Action visibility
The action has a visibility property to control the visibility & callability of service actions.
Available values:
publishedornull: public action. It can be called locally, remotely and can be published via API Gatewaypublic: public action, can be called locally & remotely but not published via API GWprotected: can be called only locally (from local services)private: can be called only internally (viathis.actions.xy()inside service)
Change visibility
module.exports = { |
The default values is
null(meanspublished) due to backward compatibility.
Action hooks
Action hooks are pluggable and reusable middleware functions that can be registered before, after or on errors of service actions. A hook is either a Function or a String. In case of a String it must be equal to service’s method name.
Before hooks
In before hooks, it receives the ctx, it can manipulate the ctx.params, ctx.meta, or add custom variables into ctx.locals what you can use in the action handlers. If there are any problem, it can throw an Error. Please note, you can’t break/skip the further executions of hooks or action handler.
Main usages:
- parameter sanitization
- parameter validation
- entity finding
- authorization
After hooks
In after hooks, it receives the ctx and the response. It can manipulate or completely change the response. In the hook, it has to return the response.
Main usages:
- property populating
- remove sensitive data.
- wrapping the response into an
Object - convert the structure of the response
Error hooks
The error hooks are called when an Error is thrown during action calling. It receives the ctx and the err. It can handle the error and return another response (fallback) or throws further the error.
Main usages:
- error handling
- wrap the error into another one
- fallback response
Service level declaration
Hooks can be assigned to a specific action (by indicating action name), all actions (*) in service or by indicating a wildcard (e.g., create-*). The latter will be applied to all actions whose name starts with create-. Action names can also be combined using a pipe symbol (e.g., create|update)
Please notice that hook registration order matter as it defines sequence by which hooks are executed. For more information take a look at hook execution order.
Before hooks
const DbService = require("moleculer-db"); |
After & Error hooks
const DbService = require("moleculer-db"); |
Action level declaration
Hooks can be also registered inside action declaration.
Please note that hook registration order matter as it defines sequence by which hooks are executed. For more information take a look at hook execution order.
Before & After hooks
broker.createService({ |
Execution order
It is important to keep in mind that hooks have a specific execution order. This is especially important to remember when multiple hooks are registered at different (service and/or action) levels. Overall, the hooks have the following execution logic:
beforehooks: global (*)->service level->action level.afterhooks: action level->service level->global (*).
When using several hooks it might be difficult visualize their execution order. However, you can set the
logLeveltodebugto quickly check the execution order of global and service level hooks.
Example of a global, service & action level hook execution chain
broker.createService({ |
Output produced by global, service & action level hooks
INFO - Before all hook |
Reusability
The most efficient way of reusing hooks is by declaring them as service methods in a separate file and import them with the mixin mechanism. This way a single hook can be easily shared across multiple actions.
// authorize.mixin.js |
// posts.service.js |
Local Storage
The locals property of Context object is a simple storage that can be used to store some additional data and pass it to the action handler. locals property and hooks are a powerful combo:
Setting ctx.locals in before hook
module.exports = { |