Download as:
Rating : ⭐⭐⭐⭐⭐
Price: $10.99
Language:EN
Pages: 159

Userid ctx return posts register new gauge metric this type gauge

Create a ServiceBroker
Quick tip: You don’t need to create manually ServiceBroker in your project. Use the Moleculer Runner to create and

execute a broker and load services. Read more about Moleculer Runner.

const { ServiceBroker } = require("moleculer");

const broker = new ServiceBroker({

Metadata option

Use metadata property to store custom values. It can be useful for a custom or

Ping

To ping remote nodes, use broker.ping method. You can ping a node, or all available nodes. It returns

{

}

Output

Output

{

Properties of ServiceBroker

broker.options

Bluebird Promise class.

broker.started

Namespace

broker.nodeID

Instance ID

broker.metadata

Logger class of ServiceBroker

broker.cacher

Serializer instance.

broker.validator

Local services.

broker.metrics

Built-in Tracer instance

broker.errorRegenerator

Boolean Checks if broker is listening to an event.

Array<Object> Returns all registered event listeners for an

Call an async hook in the registered

broker.callMiddlewareHook(name, args, opts)

The global error handler is generic way to handle exceptions. It catches the unhandled errors of action & event handlers.

Catch, handle & log the error

event definition.

Configuration

contextParamsCloning: Boolean - Cloning the params of context if enabled. High performance impact. Use it with caution! Default: false
dependencyInterval: Configurable interval (defined in ms) that’s used by the services while waiting for dependency services. Default: 1000
maxCallLevel: Number - Limit of calling level. If it reaches the limit, broker will throw
an MaxCallLevelError error. (Infinite loop protection) Default: 0
heartbeatInterval: Number - Number of seconds to send heartbeat packet to other nodes. Default: 5

{

requestTimeout: 5000,
retryPolicy: {
enabled: true,
retries: 5,
delay: 100,
maxDelay: 1000,
factor: 2,
check: err => err && !!err.retryable

},

registry: {
strategy: "RoundRobin",
preferLocal: true

},

transit: {
maxQueueSize: 50 * 1000,
disableReconnect: false,
disableVersionCheck: false,
packetLogFilter: ["HEARTBEAT"]

},

metrics: {
enabled: true,
reporter: [
"Console"
]

},

},

middlewares: ["MyMiddleware"],

replDelimiter: "mol $",
replCommands: [],

started(broker) {},

stopped(broker) {}

The schema has some main parts: name, version, settings, actions, methods, events.

Simple service schema to define two

The Service has some base properties in the schema.

// posts.v1.service.js
module.exports = {
name: "posts",
version: 1

name. It can be a Number or a String.

// posts.v2.service.js
module.exports = {
name: "posts",
version: 2,
actions: {
find() {...}
}

Via API Gateway, make a request to GET /v2/posts/find.

To disable version prefixing set $noVersionPrefix: true in Service settings.

},

}

}
The

Internal

There are some internal settings which are used by core modules. These setting names start with $ (dollar sign).

// mail.service.js
module.exports = {
name: "mailer",
settings: {

$secu.user", "transport.auth.pass"],

};

these mixins with the current schema. When a service uses mixins, all properties present in the mixin will be “mixed”

into the current service.

The above example creates an api service which inherits all properties from ApiGwService but overwrite the port

setting and extend it with a new myAction action.

hooks Deep extend with

methods Merge & overwrite.

any custom Merge & overwrite.

Merge algorithm examples

// math.service.js
module.exports = {
name: "math",
actions: {
// Shorthand definition, only a handler function
add(ctx) {
return Number(ctx.params.a) + Number(ctx.params.b); },

};

}

Inside actions, you can call other nested actions in other services with ctx.call method. It is an alias to broker.call, but it sets itself as parent context (due to correct tracing chains).

// posts.service.js
module.exports = {
name: "posts",
actions: {
async get(ctx) {
// Find a post by ID
let post = posts[ctx.params.id];

In action handlers the this is always pointed to the Service instance.

You can subscribe to events under the events key.

// Subscribe to all "user.*" events
"user.*"(ctx) {
console.log("Payload:", ctx.params);
console.log("Sender:", ctx.nodeID);
console.log("Metadata:", ctx.meta);
console.log("The called event name:", ctx.eventName); }

};

}

// payment.service.js
module.exports = {
name: "payment",
events: {
"order.created": {
// Register handler to the "other" group instead of "payment" group.

group: "other",
handler(payload) {
// ...

// mailer.service.js
module.exports = {
name: "mailer",
actions: {
send(ctx) {
// Call the `sendMail` method
return this.sendMail(ctx.params.recipients, ctx.params.subject, ctx.params.body);
}
},

methods: {
// Send an email to recipients
sendMail(recipients, subject, body) {
return new Promise((resolve, reject) => { ...

// posts.service.js
module.exports = {

name: "posts",

};
}

because these words are reserved in the schema.

In methods the this is always pointed to the Service instance.

merged() {
// Fired after the service schemas merged and before the service instance created

},

If your service depends on other services, use the dependencies property in the schema. The service waits for

dependent services before calls the started lifecycle event handler.

}

Wait for services via ServiceBroker

To wait for services, you can also use the waitForServices method of ServiceBroker. It returns a Promise which will

Set timeout & interval

broker.waitForServices("accounts", 10 * 1000, 500).then(() => { // Called if `accounts` service becomes available in 10 seconds }).catch(err => {
// Called if service is not available in 10 seconds
});

module.exports = {

name: "posts",

},

};

actions: { ... }

Name Type Description

this.name String Name of service (from schema)

this.schema Object Schema definition of service

this.broker ServiceBroker Instance of broker

this.currentContext Context Get or set the current Context object.

Service

name: "math",

actions: {

});

Load service from

Load it with broker:

// Create broker

instance of Service.

const { Service } = require("moleculer");

// Export a function, the `loadService` will call with the ServiceBroker instance. module.exports = function() {

let users = [. ... ];

If you have many services (and you will have) we suggest to put them to a services folder and load all of them with

the broker.loadServices method.

Local

If you would like to use local properties/variables in your service, declare them in the created event handler.

started() {
// Listening...

this.server.listen(this.settings.port);

}

Naming restriction

It is important to be aware that you can’t use variable name which is reserved for service or coincides with

Native ES6 classes with schema

Define actions and events handlers as class methods and call the parseServiceSchema method in constructor with

this.parseServiceSchema({
name: "greeter",
version: "v2",
meta: {
scalable: true
},
dependencies: [
"auth",
"users"

],

Need a compiler

Please note, you must use Typescript or Babel to compile decorators.

}

// With options
@Action({
cache: false,
params: {
a: "number",
b: "number"
}
})
Login2(ctx) {
//...

}

hello() { // Private

}

}

stopped() { // Reserved for moleculer, fired when stopped //...

disable them by setting internalServices: false in broker options.

List of

withServices Boolean false List with services.

onlyAvailable Boolean false List only available nodes.

Name Type Default Description

onlyLocal Boolean false List only local services.

It lists all registered actions (local & remote).

broker.call("$node.actions").then(res => console.log(res));

skipInternal Boolean false Skip the internal actions ($node).

withEndpoints Boolean false List with endpoints (nodes).

It has some options which you can declare within params.

Options

onlyAvailable Boolean false List only available subscriptions.

List of metrics

Name

Type Default Description

types

String or Array null

broker.call("$node.health").then(res => console.log(res)); Example health info:

}

´s internalServices option.

moleculer.config.js
ule.exports = {
nodeID: "node-1",
logger: true,
internalServices: {

It has request parameters & returns response, like a HTTP request.

If you have multiple instances of services, the broker will load balance the request among instances.

The actionName is a dot-separated string. The first part of it is the service name, while the second part of it represents the action name. So if you have a posts service with a create action, you can call it as posts.create.

The params is an object which is passed to the action as a part of the Context. The service can access it via ctx.params. It is optional. If you don’t define, it will be {}.

Name
Description
null

Parent Context instance. Use it to chain the calls.

Request ID or Correlation ID. Use it for tracing.

Object {}

Context null

requestID

const res = await broker.call("user.get", { id: 3 });

Call with calling options

const res = await broker.call("$node.health", null, { nodeID: "node-21" })

Metadata

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.

console.log(ctx.meta);

// Prints: { a: "John", b: 5 }

broker.createService({
name: "mod",
actions: {
hello(ctx) {
console.log(ctx.meta);
// Prints: { user: 'John' }
ctx.meta.age = 123
return this.actions.subHello(ctx.params, { parentCtx: ctx }); },

subHello(ctx) {
console.log("meta from subHello:", ctx.meta); // Prints: { user: 'John', age: 123 }
return "hi!";
}
}

Example

// moleculer.config.js
module.exports = {
nodeID: "node-1",
requestTimeout: 3000

Calling examples

// It uses the global 3000 timeout
await broker.call("greeter.normal");
// It uses the 5000 timeout from action definition
await broker.call("greeter.slow");
// It uses 1000 timeout from calling option
await broker.call("greeter.slow", null, { timeout: 1000 });

meta: { token: '63f20c2d-8902-4d86-ad87-b58c9e2333c2' } }
);

mcall with Object and options.meta

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.

]

{ status: "fulfilled", value: [/*... response of `posts.find`...*/] }, { status: "fulfilled", value: [/*... response of `users.find`...*/] }, { status: "rejected", reason: {/*... Rejected response/Error`...*/} }

const stream = fs.createReadStream(fileName);

broker.call("storage.save", stream, { meta: { filename: "avatar-123.jpg" }});

Receiving a stream in a service

module.exports = {
name: "storage",
actions: {
save(ctx) {
// Save the received stream to a file
const s = fs.createWriteStream(`/tmp/${ctx.meta.filename}`); ctx.params.pipe(s);
}
}

Process received stream on the caller side

const filename = "avatar-123.jpg";
broker.call("storage.get", { filename })
.then(stream => {
const s = fs.createWriteStream(`./${filename}`);

},

};

}

The default values is null (means published) due to backward compatibility.

Action 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:

property populating
remove sensitive data.

Main usages:

error handling
wrap the error into another one
fallback response

Please notice that hook registration order matter as it defines sequence by which hooks are executed.

module.exports = {
name: "posts",
mixins: [DbService]
hooks: {
before: {
// Define a global hook for all actions
// The hook will call the `resolveLoggedUser` method.

"*": "resolveLoggedUser",

const DbService = require("moleculer-db");

module.exports = {
name: "users",
mixins: [DbService]
hooks: {
after: {
// Define a global hook for all actions to remove sensitive data "*": function(ctx, res) {
// Remove password
delete res.password;

return res;
}
],
// Applies to all actions that start with "create-"
"create-*": [
async function (ctx, res){}
],
// Applies to all actions that end with "-user"
"*-user": [
async function (ctx, res){}
],
},
error: {
// Global error handler
"*": function(ctx, err) {
this.logger.error(`Error occurred when '${ctx.action.name}' action was called`, err);

};
}

Before & After hooks

broker.createService({
name: "greeter",
actions: {
hello: {
hooks: {
before(ctx) {
broker.logger.info("Before action hook"); },
after(ctx, res) {
broker.logger.info("After action hook")); return res;
}

});
}

Execution

It is important to keep in mind that hooks have a specific execution order. This is especially important to remember

When using several hooks it might be difficult visualize their execution order. However, you can set

the logLeve o quickly check the execution order of global and service level hooks.

actions: {
hello: {
hooks: {
before(ctx) {
broker.logger.info(chalk.yellow.bold(" },
after(ctx, res) {
broker.logger.info(chalk.yellow.bold(" return res;
}
},

Before action hook"));

});
}

}

Output produced by global, service & action level hooks

// authorize.mixin.js
module.exports = {
methods: {
checkIsAuthenticated(ctx) {
if (!ctx.meta.user)
throw new Error("Unauthenticated");
},
checkUserRole(ctx) {
if (ctx.action.role && ctx.meta.user.role != ctx.action.role) throw new Error("Forbidden");
},
checkOwner(ctx) {
// Check the owner of entity
}
}
}

// posts.service.js

};

actions: {
find: {
// No required role
handler(ctx) {}
},
create: {
role: "admin",
handler(ctx) {}

Setting ctx.locals in before hook

module.exports = {
name: "user",

Events

Broker has a built-in event bus to support Event-driven architecture and to send events to local and remote services.

the user.created event, only one users and one payments service instance will receive the event.

Emit balanced events

Send balanced events with broker.emit function. The first parameter is the name of the event, the second parameter is

// Only the `mail` & `payments` services receives it broker.emit("user.created", user, ["mail", "payments"]);

Broadcast

broker.broadcast("config.changed", config); Specify which groups/services shall receive the event:

// Send to all "mail" service instances
broker.broadcast("user.created", { user }, "mail"); // Send to all "user" & "purchase" service instances.

Subscribe to

The v0.14 version supports Context-based event handlers. Event context is useful if you are using event-driven

signature "user.created"(payload) { ... }. It is capable to detect different signatures of event

handlers:

You can also force the usage of the new signature by setting context: true in the event declaration

Context-based event handler & emit a nested event

Subscribe to events in ‘events’ property of services. Use of wildcards (?, *, **) is available in event names.

module.exports = {
events: {
// Subscribe to `user.created` event
"user.created"(ctx) {
console.log("User created:", ctx.params);

} }

Like in action definition, you should define params in even definition and the built-in Validator validates the

parameters in events.

The broker broadcasts some internal events. These events always starts with $ prefix.

The broker sends this event if the local node or a remote node loads or destroys services.

The broker sends this event when the circuit breaker module change its state to open.

Payload

The broker sends this event when the circuit breaker module change its state to half-open.

Payload

The broker sends this event when a node connected or reconnected.

Payload

Name Type Description
node Node Node info object

The broker sends this event when a node disconnected (gracefully or unexpectedly).

Name Type
Node

unexpected Boolean true - Not received heartbeat, false - Received DISCONNECT message from node.

The broker sends this event once broker.start() is called and all local services are started.

Event payload

{
"error": "<the error object with all properties>"
"module": "broker" // Name of the module where the error happened "type": "error-type" // Type of error. Full of error types: https://github.com/moleculerjs/moleculer/blob/master/src/constants.js }

Event payload

{
"error": "<the error object with all properties>"
"module": "transit" // Name of the module where the error happened "type": "error-type" // Type of error. Full of error types: https://github.com/moleculerjs/moleculer/blob/master/src/constants.js }

Event payload

{

}

Context

ctx.id String Context ID

ctx.broker ServiceBroker Instance of the broker.

ctx.eventType String Type of event (“emit” or “broadcast”).

ctx.eventGroups Array<String> Groups of event.

Methods of Context

ctx.startSpan(name, opts) Span Creates a new child span.

ctx.finishSpan(span) void Finishes a span.

Enable context tracking & change the timeout value

const broker = new ServiceBroker({
nodeID: "node-1",
tracking: {
enabled: true,
shutdownTimeout: 10 * 1000
}
});

Broker

This section describes what happens when the broker is starting & stopping.

Stopping

When you call broker.stop or stop the process, at first broker publishes an empty service list to remote nodes, so they will route the requests to other instances instead of services that are stopping. Next, the broker starts all local services. After that, the transporter disconnects and process exits.

at broker.createService or broker.loadService).

You can use it to create other module instances (e.g. http server, database modules) and store them in this.

started event

This handler is triggered when the broker.start is called and the broker starts all local services. Use it to connect to

This handler is triggered when the broker.stop is called and the broker starts stopping all local services. Use it to close

database connections, close sockets…etc.

This handler is called after the service schemas (including has been merged but before service is registered. It

means you can manipulate the merged service schema before it’s processed.

};

},
handler(ctx) {
// ...

}

}

wrapper functions. It allows to wrap action handlers, event handlers, broker methods and hook lifecycle events.

Example

Some hooks are wrappers. It means that you can wrap the original handler and return a new Function.

Wrap hooks are which the first parameter is next.

const MyValidator = {
localAction(next, action) {
// Wrap with a param validator if `action.params` is defined if (_.isObject(action.params)) {
return ctx => {
this.validate(action.params, ctx.params);
return next(ctx);
};
}

};

}

original handler. If the params property is not defined, it simply returns the original handler (skip wrapping).

If you don’t call the original next in the middleware it will break the request. It can be used in cachers. For

// Call the next

const result = await next(ctx);

Decorate core modules (extend

Middleware functions can be used to add new features to ServiceBroker or Service classes.

const res = await broker.allCall("$node.health");

localAction(next, action)

}

remoteAction(next, action)

This hook wraps the remote action handlers.

This hook wraps the local event handlers.

// my.middleware.js
module.export = {

}

// my.middleware.js
module.exports = {

name: "MyMiddleware",

This hook wraps the broker.destroyService method

}

// my.middleware.js
module.export = {
name: "MyMiddleware",

}

This hook wraps the broker.emit method.

// my.middleware.js
module.export = {
name: "MyMiddleware",

}

broadcast(next) {
return function(eventName, payload, opts) {
console.log("The 'broadcast' is called.", eventName); return next(eventName, payload, opts);

}

};

This hook is called after local service creating.

// my.middleware.js
module.export = {
name: "MyMiddleware",

}

// my.middleware.js
module.export = {
name: "MyMiddleware",

}

serviceStarted(service) {
console.log("Service started", service.fullName);

}

}

serviceStopped(service) {
console.log("Service stopped", service.fullName);

}

serviceCreating(service, schema)

}

}

This hook is called before sending a communication packet.

// my.middleware.js
module.export = {

name: "MyMiddleware",

}

name: "MyMiddleware",

}

newLogEntry(type, args, bindings) (sync)

This hook is called when a new log messages iscreated.

created(broker)

This hook is called when broker created.

}

This hook is called before broker starting.

// my.middleware.js
module.export = {
name: "MyMiddleware",

// my.middleware.js
module.export = {
name: "MyMiddleware",

}

started(broker) {
console.log("Broker started"); }

stopped(broker)

}

Many integrated features have been exposed as internal middlewares. These middlewares are loaded by default when broker is created. However, they can be turned off by setting the internalMiddlewares: false in broker option. In this case you must explicitly specify the required middlewares in the middlewares: [] broker option.

Internal middlewares

Cacher Optional Cacher middleware.

ContextTracker Optional Context tracker feature.

ErrorHandler AlwaysError handling.

Metrics Optional Metrics feature.

Debugging.TransitLogger Optional Transit Logger.

Debugging.ActionLogger Optional Action logger.

This middleware uses built-in Node crypto lib.

// moleculer.config.js

middlewares: [

Middlewares.Transmit.Encryption("secret-password", "aes-256-cbc", initVector) //

This middleware uses built-in Node zlib lib.

// moleculer.config.js

Middlewares.Transmit.Compression("deflate") // or "deflateRaw" or "gzip"

]

// moleculer.config.js

const { Middlewares } = require("moleculer");

logPacketData: false,

folder: null,

packetFilter: ["HEARTBEAT"]

})

logger Object or Function null Logger class.

logLevel String info Log level for built-in console logger.

color.send String grey Supports all

packetFilter Array<String> HEARTBEAT Type of o skip

// Create broker

module.exports = {

folder: null,

colors: {

})

]

logLevel String info Log level for built-in console logger.

logParams Boolean falseLogs request parameters

color.response String cyan Supports all

Event Execution

handler(ctx) { /* ... */}
}
}
};

Unlike throttling, debouncing is a technique of keeping th 0 until a period of calm, and then

Loading &

If you want to use the built-in middlewares use their names in middlewares[] broker option. Also,

// Extend with custom middleware
Middlewares.MyCustom = {
created(broker) {
broker.logger.info("My custom middleware is created!"); }

};

nodes. These message brokers mainly support publish/subscribe messaging pattern.

Transporter is an important module if you are running services on multiple nodes. Transporter communicates with other

There are several built-in transporters in Moleculer framework.

TCP

be a static list in your configuration or a file path which contains the list.

Use TCP transporter with default options

// UDP port
udpPort: 4445,
// UDP bind address (if null, bind on all interfaces) udpBindAddress: null,
// UDP sending period (seconds)
udpPeriod: 30,

// Multicast address.

};
}

// Gossip sending period in seconds
gossipPeriod: 2,
// Maximum enabled outgoing connections. If reach, close the old connections maxConnections: 32,
// Maximum TCP packet size
maxPacketSize: 1 * 1024 * 1024

TCP transporter with static endpoint list

It needs to start with tcp://.

// moleculer.config.js
module.exports = {
nodeID: "node-1",
transporter: "tcp://172.17.0.1:6000/node-1,172.17.0.2:6000/node- 2,172.17.0.3:6000/node-3"

]

Serviceless node

NATS

Built-in transporter for

To use this transporter install the nats module with npm install nats --save command.

Connect to ‘nats://localhost:4222’

Connect to a remote NATS server with auth

// moleculer.config.js
module.exports = {
transporter: "nats://user:pass@nats-server:4222" };

// moleculer.config.js
module.exports = {

transporter: {

// moleculer.config.js
module.exports = {
nodeID: "server-1",
transporter: "redis://redis.server:6379"
};

Dependencies

Connect with connection string

// moleculer.config.js
module.exports = {
transporter: "redis://localhost:6379"
};

};

Connect to Redis cluster

Dependencies

To use this transporter install the mqtt module with npm install mqtt --save command.

// moleculer.config.js
module.exports = {
transporter: "mqtt://mqtt-server:1883"
};

Connect to secure MQTT server

AMQP (0.9)

Built-in transporter for 0.9 protocol (e.g.: .

Options can be passed to amqp.connect() method.

Connect to ‘amqp://guest:guest@localhost:5672’

// moleculer.config.js
module.exports = {
transporter: "amqps://rabbitmq-server:5672" });

Connect to a remote server with options & credentials

// moleculer.config.js
module.exports = {
transporter: "amqp10://activemq-server:5672" };

Dependencies

Connect to ‘amqp10://guest:guest@localhost:5672’

// moleculer.config.js
module.exports = {
transporter: "AMQP10"
};

};

Kafka

To use this transporter install the kafka-node module with npm install kafka-node --save command.

Connect to Zookeeper

// KafkaClient options. More info: https://github.com/SOHU-Co/kafka- node#clientconnectionstring-clientid-zkoptions-noackbatchoptions-ssloptions client: {
zkOptions: undefined,
noAckBatchOptions: undefined,
sslOptions: undefined

},

Built-in transporter for

It is a simple implementation. It transfers Moleculer packets to consumers via pub/sub. There are not

--save command.

Connect with default settings

// moleculer.config.js
module.exports = {
transporter: {
type: "STAN",
options: {
url: "stan://127.0.0.1:4222",
clusterID: "my-cluster"
}
}
};

Custom transporter

class MyTransporter extends BaseTransporter {
connect() { /*...*/ }
disconnect() { /*...*/ }
subscribe() { /*...*/ }
send() { /*...*/ }
}

Use custom

Some transporter servers have built-in balancer solution. E.g.: RabbitMQ, NATS, NATS-Streaming. If you want to use

the transporter balancer instead of Moleculer balancer, set the disableBalancer broker option to true.

If you disable the built-in Moleculer balancer, all requests & events will be transferred via transporter

(including local requests). E.g. you have a local math service and you call math.add locally, the request will

Note that certain data types (e.g., Date, Map, BigInt) cd with native JSON serializer. If

you are working with this kind of data consider using or any other binary serializer.

This is the default serializer. It serializes the packets to JSON string and deserializes the received data to packet.

// moleculer.config.js
module.exports = {
serializer: "JSON" // not necessary to set, because it is the default };

To use this serializer install the avsc module with npm install avsc --save command.

MsgPack serializer

};

Notepack
Built-in erializer.

// moleculer.config.js
module.exports = {
serializer: "Notepack"
};
Dependencies
To use this serializer install the notepack module with npm install notepack.io --save command.

CBOR serializer
CBOR ) is the han any other serializers.

Example
// moleculer.config.js
module.exports = {
logger: true,
serializer: "CBOR"
};
Custom

Use custom

// moleculer.config.js
const MySerializer = require("./my-serializer");

Local discovery (default option) uses the module to exchange node info and heartbeat packets (for more info about packet structure check . It’s the simplest and the fastest among the available discovery mechanisms as it doesn’t require any external solutions. However, this discovery method also has some drawbacks, especially for large scale deployments with >100 nodes. The heartbeat packets can generate large amount traffic that can saturate the communication bus and, therefore, deteriorate the performance of actions and events, i.e., slow down the delivery of request/response and event packets.

Please note the TCP transporter uses Gossip protocol & UDP packets for discovery & heartbeats, it means it can work only with local discovery mechanism.

registry: {
discoverer: {
type: "Local",
options: {
// Send heartbeat in every 10 seconds
heartbeatInterval: 10,

// Heartbeat timeout in seconds
heartbeatTimeout: 30,

This approach reduces the load over the transporter module, it’s used exclusively for the exchange of the request,

response, event packets.

periodicity depends on the heartbeatInterval broker option.

To use Redis discovery install the ioredis module with the npm install ioredis --save command.

// moleculer.config.js
module.exports = {
registry: {
discoverer: "redis://redis-server:6379"
}

}

}

// Serializer
serializer: "JSON",

// Full heartbeat checks. It generates more network traffic // 10 means every 10 cycle.

// Send heartbeat in every 10 seconds
heartbeatInterval: 10,

// Heartbeat timeout in seconds
heartbeatTimeout: 30,

Etcovery d is very similar to t stores heartbeat and discovery packets

at etcd3’s option will remove heartbeat info of nodes that have crashed or disconnected from the

Example to connect local etcd3 server

// moleculer.config.js
module.exports = {
registry: {
discoverer: "Etcd3"
}

Example with options

// moleculer.config.js
module.exports = {
registry: {
discoverer: {
type: "Etcd3",
options: {
etcd: {
// etcd3 connection options.

fullCheck: 10,

// --- COMMON DISCOVERER OPTIONS ---

} } } }

// Remove offline nodes after 10 minutes cleanOfflineNodesTimeout: 10 * 60

Tip: To further reduce network traffic use nstead of JSON.

Moleculer has a built-in service registry module. It stores all information about services, actions, event listeners and

nodes. When you call a service or emit an event, broker asks the registry to look up a node which is able to execute the

Built-in

To configure strategy, set strategy broker options under registry property. It can be either a name (in case of built-in

// moleculer.config.js
module.exports = {
registry: {
strategy: "RoundRobin"
}

};

};

CPU usage-based

}

};

only ping one node / host. Since the node list can be very long, it gets samples and selects the host with the lowest

latency only from a sample instead of the whole node list.

Name

TypeDefault

Description

sampleCount Number 5

lowLatency

Number 10

Sharding strategy

Shard invocation strategy is based on consistent-hashing algorithm. It uses a key value from context params or meta to

Example of a shard key user.id in context meta

// moleculer.config.js
module.exports = {
registry: {
strategy: "Shard",
strategyOptions: {
shardKey: "#user.id"
}
}
};

vnodes Number 10 Number of virtual nodes

ringSize Number 2^32 Size of the ring

You can overwrite globally defined load balancing strategy in action/event definitions.

Using ‘Shard’ strategy for ‘hello’ action instead of global ‘RoundRobin’

Custom strategy can be created. We recommend to copy the source of RandomStrategy and implement

the select method.

module.exports = MyStrategy;

Use custom strategy

The ServiceBroker first tries to call the local instances of service (if exists) to reduce network latencies. It means, if the

given service is available on the local broker, the configured strategy will be skipped and the broker will call the local

Fault tolerance

Moleculer has several built-in fault-tolerance features. They can be enabled or disabled in broker options.

The Circuit Breaker can prevent an application from repeatedly trying to execute an operation that’s likely

to fail. Allowing it to continue without waiting for the fault to be fixed or wasting CPU cycles while it

Enable it in the broker options

const broker = new ServiceBroker({
circuitBreaker: {
enabled: true,
threshold: 0.5,
minRequestCount: 20,

Name Type Default

Minimum request count. Below it, CB does not trip. Number of seconds for time window.

Number of milliseconds to switch from open to half- open state

Number

0.5

60

halfOpenTime

If the circuit-breaker state is changed, ServiceBroker will send internal events.

These global options can be overridden in action definition, as well.

Retry

Name Type Default Description

enabled Boolean false Enable feature.

check Function err && !!err.retryable A function to check failed requests.

Overwrite the retries value in calling option

};

}

Timeout

});

Overwrite the timeout value in calling option

call has already been rejected with a RequestTimeoutError error.

Bulkhead feature is implemented in Moleculer framework to control the concurrent request handling of actions.

Name Type Default Description

enabled Boolean falseEnable feature.

throw QueueIsFull exception for every addition requests.

Action

Events

Event handlers also support eature.

Fallback feature is useful, when you don’t want to give back errors to the users. Instead, call an other action or return

some common content. Fallback response can be set in calling options or in action definition. It should be

});

Fallback in action

Fallback as a function

module.exports = {
name: "recommends",
actions: {
add: {
fallback: (ctx, err) => "Some cached result", handler(ctx) {
// Do something
}
}
}
};

};

Caching

Moleculer has a built-in caching solution to cache responses of service actions. To enable it, set a cacher type in

});

// Create a service
broker.createService({
name: "users",
actions: {
list: {
// Enable caching to this action
cache: true,
handler(ctx) {
this.logger.info("Handler called!"); return [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" }
]
}
}
}

Console messages:

[2017-08-18T13:04:33.845Z] INFO dev-pc/BROKER: Broker started. [2017-08-18T13:04:33.848Z] INFO dev-pc/USERS: Handler called! [2017-08-18T13:04:33.849Z] INFO dev-pc/BROKER: Users count: 2

The cacher generates key from service name, action name and the params of context.

The syntax of key is:

posts.find:limit|5|offset|20

The params object can contain properties that are not relevant for the cache key. Also, it can cause performance issues if

{
name: "posts",
actions: {
list: {
cache: {
// generate cache key from "limit", "offset" params and "user.id" meta keys: ["limit", "offset","#user.id"]
},
handler(ctx) {
return this.getList(ctx.params.limit, ctx.params.offset);
}
}
}
}

// If params is { limit: 10, offset: 30 } and meta is { user: { id: 123 } }, // the cache key will be:
// posts.list:10|30|123

concatenated params in the key with maxParamsLength cacher option. When the key is longer than the configured limit

value, the cacher calculates a hash (SHA256) from the full key and adds it to the end of the key.

Generate a limited-length key

const broker = new ServiceBroker({

Conditional caching allows to bypass the cached response and execute an action in order to obtain “fresh” data.

To bypass the cache set ctx.meta.$cache to false before calling an action.

flag within the request.

Example of a custom conditional caching function

}
};

// Use custom `enabled` function to turn off caching for this request broker.call("greeter.hello", { name: "Moleculer", noCache: true }))

Default TTL setting can be overriden in action definition.

}
}
}
});

Custom

// Save to cache
broker.cacher.set("mykey.a", { a: 5 });

// Get from cache (async)

await broker.cacher.clean("mykey.**");

// Clean all entries

Clear

When you create a new model in your service, you have to clear the old cached model entries.

this.broker.cacher.clean("users.**");

// Clear multiple cache entries
this.broker.cacher.clean([ "users.**", "posts.**" ]);

} } }

only required for non-centralized cachers like Memory or MemoryLRU.

Example

methods: {
cleanCache() {
// Broadcast the event, so all service instances receive it (including this

}

Clear cache among different services

Service dependency is a common situation. E.g. posts service stores information from users service in cached entries (in case of populating).

The author field is received from users service. So if the users service clears cache entries, the posts service has to clear own cache entries, as well. Therefore you should also subscribe to the cache.clear.users event in posts service. To make it easier, create a CacheCleaner mixin and define in the dependent services schema.

cache.cleaner.mixin.js

const CacheCleaner = require("./cache.cleaner.mixin"); module.exports = {
name: "posts",
mixins: [CacheCleaner([
"users",
"posts"
])],
actions: {
//...

}

Moleculer also supports cache locking feature. For detailed info

Enable Lock

const broker = new ServiceBroker({
cacher: {
ttl: 60,
lock: {
ttl: 15, // The maximum amount of time you want the resource locked in seconds
staleTime: 10, // If the TTL is less than this number, means that the

resources are staled
}
}

seconds
staleTime: 10, // If the TTL is less than this number, means that the resources are staled
}
}

});

are staled
},
// Redlock settings
redlock: {
// Redis clients. Support node-redis or ioredis. By default will use the local client.

clients: [client1, client2, client3],
// thedetails
// see
driftF

} } }
});

Memory

MemoryCacher is a built-in memory cache module. It stores entries in the heap memory.

const broker = new ServiceBroker({
cacher: true

});

ttl Number null Time-to-live in seconds.

clone Boolean or Function falseClone the cached data when return it.

The cacher uses the lodash _.cloneDeep method for cloning. To change it, set a Function to the clone option instead

of a Boolean.

options: {

clone: data => JSON.parse(JSON.stringify(data))

LRU memory cacher is a built-in module. It deletes the least-recently-used items.

Enable LRU cacher

let broker = new ServiceBroker({

logLevel: "debug",

max: 100,

// Time-to-Live

Options

Name Type Default Description

maxParamsLength Number null Maximum length of params in generated keys.

lock Boolean or Object null Enable lock feature.

RedisCacher is a built-in based distributed cache module. It uses ibrary.

Use it, if you have multiple instances of services because if one instance stores some data in the cache, other instances

With connection string

const broker = new ServiceBroker({
cacher: "redis://redis-server:6379"

monitor: false
// Redis settings
redis: {

host: "redis-server",

const broker = new ServiceBroker({

nodeID: "node-123",

// Using MessagePack serializer to store data.

serializer: "MsgPack",

});
}

const broker = new ServiceBroker({

cacher: {

ttl Number null Time-to-live in seconds. Disabled: 0 or null

monitor Boolean falseEnable Redis client f enabled, every client

maxParamsLength Number null Maximum length of params in generated keys.

serializer String "JSON" Name of a built-in serializer.

Dependencies

To be able to use this cacher, install the ioredis module with the npm install ioredis --

Create custom

const BaseCacher = require("moleculer").Cachers.Base;

const broker = new ServiceBroker({
cacher: new MyCacher()

});

Default usage

//moleculer.config.js
module.exports = {
nodeID: "node-100",
validator: true // Using the default Fastest Validator

//moleculer.config.js
module.exports = {
nodeID: "node-100",
validator: {
type: "Fastest",

} }

options: {
useNewCustomCheckerFunction: true, defaults: { /*...*/ },
messages: { /*...*/ },
aliases: { /*...*/ }
}

const broker = new ServiceBroker({
validator: true // Default is true
});

broker.createService({
name: "say",
actions: {
hello: {
// Validator schema for params
params: {
name: { type: "string", min: 2 } },
handler(ctx) {
return "Hello " + ctx.params.name;

broker.call("say.hello", { name: "Walter" }).then(console.log) .catch(err => console.error(err.message));
// -> "Hello Walter"

Example validation schema

Find more information about validation schema in the

Async custom

Using custom async validation

// posts.service.js
module.exports = {
name: "posts",
actions: {
params: {
$$async: true,
owner: { type: "string", custom: async (value, errors, schema, name, parent, context) => {

} }

return value;
} },
},
/* ... */

Events Validation

definitions
params: {
from: "string|optional",
to: "email",
subject: "string"
},
handler(ctx) {
this.logger.info("Event received, parameters OK!", ctx.params); }
}
}
};

Custom

class MyValidator extends BaseValidator {}

module.exports = {
nodeID: "node-100",
validator: new MyValidator()
}

compile(schema) {
return (params) => this.validate(params, schema);

}

// --- TEST BROKER ---

broker.createService({
name: "greeter",
actions: {

}
}

.catch(err => broker.logger.error(err.message, err.data));

Metrics

Enable metrics & define console reporter

// moleculer.config.js

"Console"

]

reporter Object or Array<Object> null Metric reporter configuration.

collectProcessMetrics Boolean

Default bucket values for histograms. Default: [0.005,

60 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5,

10]

Default quantiles for histograms. Default: [0.5, 0.9,

0.95, 0.99, 0.999]

defaultMaxAgeSeconds
10

Number of buckets for quantile calculation.

defaultAggregator

sum

Name Type Default Description

includes String or Array<String> null List of metrics to be exported.

// moleculer.config.js
module.exports = {
metrics: {
enabled: true,
reporter: [
{
type: "Console",
options: {
includes: ["moleculer.**.total"],
excludes: ["moleculer.broker.**","moleculer.request.**"],

metricNamePrefix: "mol:", // Original "moleculer.node.type". With prefix: "mol:moleculer.node.type"
metricNameSuffix: ".value", // Original "moleculer.node.type". With prefix: "moleculer.node.type.value"

logger: null,
// Using colors
colors: true,
// Prints only changed metrics, not the full list.

onlyChanges: true
}
}
]
}
};

- "metric" - save metrics to individual files //
// - "label" - save metrics by labels to individual files mode: "metric",

};

}

] } }

// moleculer.config.js
module.exports = {
metrics: {
enabled: true,
reporter: [
{
type: "Event",
options: {
// Event name
eventName: "$metrics.snapshot", // Broadcast or emit
broadcast: false,
// Event groups
groups: null,
// Send only changed metrics onlyChanges: false,
// Sending interval in seconds interval: 5,
}
}
]
}

};

Prometheus

Prometheus reporter publishes metrics in Prometheus format. The server can collect them. Default port

// moleculer.config.js
module.exports = {
metrics: {
enabled: true,
reporter: [
{
type: "StatsD",
options: {
// Server host
host: "localhost",
// Server port
port: 8125,
// Maximum payload size.

maxPayloadSize: 1300
}
}
]
}

Create custom metrics

const BaseReporter = require("moleculer").MetricReporters.Base;

Gauge provides the following methods:
increment(labels?: GenericObject, value?: number, timestamp?: number)
decrement(labels?: GenericObject, value?: number, timestamp?: number)
set(value: number, labels?: GenericObject, timestamp?: number)
Histogram
A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a sum of all observed values and calculates configurable quantiles over a sliding time window. It can also provide 1-minute rate.

Histogram provides the following methods:
observe(value: number, labels?: GenericObject, timestamp?: number)

An info is a single string or number value like process arguments, hostname or version numbers.

process.arguments (info)
process.pid (info)
process.ppid (info)
process.eventloop.lag.min (gauge)
process.eventloop.lag.avg (gauge)
process.eventloop.lag.max (gauge)
process.eventloop.lag.count (gauge)
process.memory.heap.size.total (gauge)
process.memory.heap.size.used (gauge)
process.memory.rss (gauge)
process.memory.external (gauge)
process.memory.heap.space.size.total (gauge)
process.memory.heap.space.size.used (gauge)
process.memory.heap.space.size.available (gauge)
process.memory.heap.space.size.physical (gauge)
process.memory.heap.stat.heap.size.total (gauge)
process.memory.heap.stat.executable.size.total (gauge) process.memory.heap.stat.physical.size.total (gauge) process.memory.heap.stat.available.size.total (gauge) process.memory.heap.stat.used.heap.size (gauge)
process.memory.heap.stat.heap.size.limit (gauge)
process.memory.heap.stat.mallocated.memory (gauge) process.memory.heap.stat.peak.mallocated.memory (gauge) process.memory.heap.stat.zap.garbage (gauge)
process.uptime (gauge)
process.internal.active.handles (gauge)
process.internal.active.requests (gauge)
process.versions.node (info)
process.gc.time (gauge)
process.gc.total.time (gauge)
process.gc.executed.total (gauge)

OS metrics

New metric registration

You can easily create custom metrics.

get(ctx) {
// Increment metric
this.broker.metrics.increment("posts.get.total", 1);

};

name: "posts",

},

};

actions: {
// Create a new post
create(ctx) {
// Measure the post creation time
const timeEnd = this.broker.metrics.timer("posts.creation.time"); const post = await this.adapter.create(ctx.params);

const duration = timeEnd();

};
});

ageBuckets: 10

Errors

Parameters

Name Type Default Description

Example

const { MoleculerError } = require("moleculer").Errors;

Parameters

Name Type Default Description

Example

const { MoleculerRetryableError } = require("moleculer").Errors;

Error for client error which is not retryable. Parameters are same as .

sses

Throw it if your request is timed out.

Error code: 504
Retryable: true
Type: REQUEST_TIMEOUT

Throw it if there are too many active requests.

Error code: 429
Retryable: true
Type: QUEUE_FULL

Error code: 500
Retryable: false
Type: MAX_CALL_LEVEL

ServiceSchemaError

Error code: 500
Retryable: false
Type: BROKER_OPTIONS_ERROR

Throw it if shutdown is timed out.

Throw it if transporter receives unknown data.

Error code: 500
Retryable: false
Type: INVALID_PACKET_DATA

Preserve custom error classes while transferring between re

For this purpose provide your own Regenerator. We recommend looking at the source code of and

Create custom

const { Regenerator, MoleculerError } = require("moleculer").Errors; const { ServiceBroker } = require("moleculer");

}

module.exports = CustomRegenerator;

Use custom regenerator

Moleculer Runner is a helper script that helps you run Moleculer projects. With it, you don’t need to create a ServiceBroker instance with options. Instead, you can create a moleculer.config.js file in the root of repo with broker options. Then simply call the moleculer-runner in NPM script, and it will automatically load the configuration file, create the broker and load the services. Alternatively, you can declare your configuration as environment variables.

Production-ready

Option Type Default Description

-r, --repl Boolean falseIf true, it switches to mode after broker started.

-E, --envfile String null Load environment variables from the specified file. <file>

-i, --instances Number null Launch [number] node instances or max for all cpu cores

The start script is to load the default moleculer.config.js file if it exists, otherwise only loads options from environment variables. Starts 4 instances of broker, then they start all services from the services folder. Run it with npm start command.

Configuration loading

3.If not defined, it loads the moleculer.config.js file from the current directory. If it does not exist, it loads

the moleculer.config.json file.

To overwrite broker’s deeply nested default options, which are not present in moleculer.config.js, via

environment variables, use the L_ prefix and double underscore for nested properties in .env file. For

// moleculer.config.js
module.exports = {
nodeID: "node-test",
logger: true,
logLevel: "debug",

transporter: "nats://localhost:4222",
requestTimeout: 5 * 1000,

Moleculer Runner also supports asynchronous configuration files. In this case moleculer.config.js must export

a Function that returns a Promise (or you can use async/await).

This function runs with the MoleculerRunner instance as the this context. Useful if you need to access the

flags passed to the runner. Check the ource more details.

# Shorthand transporter
TRANSPORTER=nats://localhost:4222
REQUESTTIMEOUT=5000

# Nested property
CIRCUITBREAKER_ENABLED=true

with SERVICES and SERVICEDIR environment variables.

Loading steps:

it applies and load the found files.

Please note: shorthand names can also be used in SERVICES env var.

It loads all *.service.js files from the my-services folder (including sub-folders too).

Glob

!services/others/**/*.service.js - skip all services in the services/others folder and sub-folders.

services/others/mandatory/main.service.js - load the exact service.

.env files

Moleculer runner can load .env file at starting. There are two new cli options to load env file:

moleculer-web

The moleculer-web is the official API gateway service for Moleculer framework. Use it to publish your services as RESTful APIs.

Install

npm i moleculer-web

const { ServiceBroker } = require("moleculer");
const ApiService = require("moleculer-web");

const broker = new ServiceBroker();

Call math.add action with params: http://localhost:3000/math/add?a=25&b=13

Get health info of node: http://localhost:3000/~node/health

settings: {
routes: [{

path: "/api",

});
}]

method types.

Using named parameters in aliases is possible. Named parameters are defined by prefixing a colon to the parameter

Aliases Action

The API gateway implements listAliases that lists the HTTP endpoints to actions mappings.

});

broker.createService({
mixins: [ApiService],

broker.createService({

mixins: [ApiService],

});

E.g.: To access the broker, use req.$service.broker.

restrict - enable to request only the routes with aliases.

broker.createService({
mixins: [ApiService],

});

Example

const ApiGateway = require("moleculer-web");
module.exports = {
mixins: [ApiGateway],

aliases: {
// File upload from HTML multipart form "POST /": "multipart:file.save",

// File upload from AJAX or cURL
"PUT /:id": "stream:file.save",

});

}

] }

mappingPolicy: "restrict"

ctx.meta.$multipart contains the additional text form-data fields must be sent before other files fields.

The auto-alias feature allows you to declare your route alias directly in your services. The gateway will dynamically

// api.service.js
module.exports = {

mixins: [ApiGateway],

aliases: {
"GET /hi": "test.hello"
},

actions: {
list: {
// Expose as "/api/v2/posts/"
rest: "GET /",
handler(ctx) {}
},

get: {

},

update: {
rest: "PUT /:id",
handler(ctx) {}

},

GET
GET
GET
POST PUT

/api/hi
/api/v2/posts
/api/v2/posts/:id /api/v2/posts
/api/v2/posts/:id

settings: {
// Base path
rest: "posts/"

},

Disable

To disable parameter merging set mergeParams: false in route settings. In this case the parameters is separated.

});
}]

Un-merged req.$params:

}

More information: https://github.com/ljharb/qs

Array parameters

foo: {
bar: ["a", "b"],
baz: "c"

}

use: [
cookieParser(),
helmet()

],

import { Service, ServiceSchema } from "moleculer"; import ApiGatewayService from "moleculer-web"; const swStats = require("swagger-stats");

const swMiddleware = swStats.getMiddleware();

},

use: [swMiddleware],

async started(this: Service): Promise<void> {
this.addRoute({
path: "/",
use: [swMiddleware],
});
},

broker.createService({
mixins: [ApiService],
settings: {
// Global middlewares. Applied to all routes.

use: [
cookieParser(),
helmet()

passport.initialize(),

passport.session(),

broker.createService({
mixins: [ApiService],

settings: {
assets: {
// Root folder of assets
folder: "./assets",

});
}

set timeout, retries or fallbackResponse options for routes. Read more about calling options

Please note that you can also set the timeout for an action directly in its definition

}

broker.createService({
mixins: [ApiService],

settings: {
routes: [
{
path: "/admin",

});
] }

bodyParsers: {
json: true
}

Response type & status code

Available meta fields:

};

}

return csvFileStream;

1.Set authorization: true in your routes

2.Define the authorize method in service.

settings: {
routes: [{
// First thing
authorization: true
}]
},

methods: {
// Second thing
authorize(ctx, route, req, res) {
// Read the token from header
let auth = req.headers["authorization"];
if (auth && auth.startsWith("Bearer")) {

} } }

} else {
// No token
return Promise.reject(new E.UnAuthorizedError(E.ERR_NO_TOKEN));

}

1.Set authentication: true in your routes

2.Define your custom authenticate method in your service

mixins: ApiGatewayService,

settings: {
routes: [{
// Enable authentication
authentication: true

response data.

broker.createService({
mixins: [ApiService],

});
] }

onAfterCall(ctx, route, req, res, data) {
// Async function which return with Promise return doSomething(ctx, res, data);
}

In previous versions of Moleculer Web, you couldn’t manipulate the data in onAfterCall. Now you can,

broker.createService({
mixins: [ApiService],
settings: {

routes: [{

Error

Usage

const svc = broker.createService({
mixins: [ApiService],

allowedHeaders: [],
// Configures the Access-Control-Expose-Headers CORS header.

exposedHeaders: [],
// Configures the Access-Control-Allow-Credentials CORS header.

});

}

}]

// Route CORS settings (overwrite global settings)
cors: {
origin: ["http://localhost:3000", "https://localhost:4000"], methods: ["GET", "OPTIONS", "POST"],
credentials: true
},

settings: {
rateLimit: {
// How long to keep record of requests in memory (in milliseconds).

// Defaults to 60000 (1 min)

}

}

}

etag: true
}
]
}

Custom etag Function

module.exports = {
name: "export",
actions: {
// Download response as a file in the browser downloadCSV(ctx) {
ctx.meta.$responseType = "text/csv"; ctx.meta.$responseHeaders = {
"Content-Disposition": `attachment; filename="data-${ctx.params.id}.csv"`,
"ETag": '<your etag here>'
};
return csvFileStream;
}
}
}

HTTP2

port: 8443,

// HTTPS server with certificate
https: {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem")

You can use Moleculer-Web as a middleware in an application.

Usage

});

// Use ApiGateway as middleware
app.use("/api", svc.express());

// Listening
app.listen(3000);

// Exposed port
port: 3000,

// Exposed IP
ip: "0.0.0.0",

// Global-level middlewares
use: [
compression(),
cookieParser()

],

// Optimize route & alias paths (deeper first). optimizeOrder: true,

// Routes
routes: [
{
// Path prefix to this route (full path: /api/admin ) path: "/admin",

],

// Action aliases
aliases: {
"POST users": "users.create",
"health": "$node.health"

// Whitelist of actions (array of string mask or regex) whitelist: [
"posts.*",
"file.*",
/^math\.\w+$/

// Use bodyparser module
bodyParsers: {
json: false,
urlencoded: { extended: true }

},

],

res.setHeader("X-Custom-Header", "123456"); return data;

},

Service

This service method (this.addRoute(opts, toBottom = true)) add/replace a route. For example, you can call it from your mixins to define new routes (e.g. swagger route, graphql route, etc.).

open HTTPS server

aliases

multiple body-parsers

simple server with RESTful aliases

start socket.io websocket server

static files

middlewares

multiple body-parsers

before & after hooks

webpack config file

compression

Hot-replacement

Babel, SASS, SCSS, Vue SFC

Usage

Switch broker to REPL mode

});

REPL Commands

broadcastLocal <eventName> Broadcast an event locally

cache Manage cache

destroy <serviceName> Destroy a local service

emit <eventName> Emit an event

load <servicePath> Load a service from file

loadFolder <serviceFolder> [fileMask] Load all services from folder

help [command] display help for command

List

-f, --filter <match>filter nodes (e.g.: 'node-*')

--raw print service registry to JSON

List

-f, --filter <match>filter services (e.g.: 'user*')

-i, --skipinternal skip internal services

List

-f, --filter <match>filter actions (e.g.: 'users.*')

-i, --skipinternal skip internal actions

List

-f, --filter <match>filter event listeners (e.g.: 'user.*')

-i, --skipinternal skip internal event listeners

Show common
mol $ info

Output

Call an action with params, meta & options

mol $ call "math.add" --a 5 --#b Bob --$timeout 1
Params will be { a: 5 }, meta will be { b: 'Bob' } and options will be { timeout: 1 }.

mol $ call "math.add" --load my-params.json
It tries to load the my-params.jon file to params.

Call with file stream

Direct

Get health info from node-12 node

mol $ emit "user.created" --a 5 --b Bob --c --no-d --e.f "hello" Params will be { a: 5, b: 'Bob', c: true, d: false, e: { f: 'hello' } }

Emit an event with params &

Parameters

Please note, parameters can be passed only as JSON string.
mol $ bench math.add '{ "a": 50, "b": 32 }'
Load a service from file

Cache
You can list keys of cache entries with
mol $ cache keys
Options
-f, --filter <match>filter keys
Cache
You clear the cache with:
mol $ cache clear
that by default removes all the entries. If you want to remove a subset of entries, you must add a pattern: Clear with pattern
mol $ cache clear greeter.*
Event listener
REPL can subscribe and listen to events. To subscribe use:
mol $ listener add user.created
Subscribe with group option
mol $ listener add user.created --group abcd
To unsubscribe use:

mol $ listener remove user.created

// moleculer.config.js
module.exports = {
replCommands: [
{
command: "hello <name>",
description: "Call the greeter.hello service with name",
alias: "hi",
options: [
{ option: "-u, --uppercase", description: "Uppercase the name" } ],
types: {
string: ["name"],
boolean: ["u", "uppercase"]
},
//parse(command, args) {},
//validate(args) {},
//help(args) {},
allowUnknownOptions: true,
action(broker, args/*, helpers*/) {
const name = args.options.uppercase ? args.name.toUpperCase() : args.name; return broker.call("greeter.hello", { name }).then(console.log); }
}
]
};

mol $ hello -u John
Hello JOHN

$ npm i -g moleculer-cli

Commands

and generates a new module to the ./my-project folder.

Answers from

You can disable the automatic NPM dependency installation with --no-install argument. It can be useful to generate project programmatically.

$ moleculer init project my-project --answers ./answers.json --no-install

Docker & Docker Coe files

tests & c with

tests & c with

lint with

readme skeleton

tests & coverage with

The shorthand repo notation is passed to o it can be bitbucket:username/repo for a Bitbucket repo and username/repo#branch for tags or branches.

Local Templates

$ moleculer alias-template myAlias somegithubuser/reponame
$ moleculer alias-template otherAlias ./path/to/some-local/custom/template

$ moleculer init myAlias my-project

The meta.js file exports a function that returns an object defining the Moleculer CLI init interface. The function takes a

parameter values that gives access to external values passed in from the CLI. The object has several keys which are

The before function executes before the transformation is run, the after function executes after the transformation is

run, and the complete function executes after the transformation is run and the files are copied to the destination

The filters object takes a set of keys matching a path and a value matching the name of a question variable. If the

question variable’s value is false, the specified path will be ignored during the transformation and those files will not

Handlebars can also transform file names.

This command starts a new ServiceBroker locally and switches to REPL mode.

--config, -cLoad configuration from a file [string] [default: ""]

--ns Namespace [string] [default: ""]

[string] [default: null]

Connect

$ moleculer connect nats://localhost:4222

# Connect to Redis

$ moleculer connect amqp://localhost:5672

# Load all options from config file

--config, -cLoad configuration from a file [string] [default: ""]

--ns Namespace [string] [default: ""]

--commands Custom REPL command file mask (e.g.: ./commands/*.js)

[string] [default: null]

Options

--version Show version number [boolean]

--ns Namespace [string] [default: ""]

--level Logging level [string] [default: "silent"]

Example with params & meta

moleculer call math.add --transporter NATS --@a 5 --@b 3 --#meta-key MyMetaValue

TRANSPORTER=nats://localhost:42222 moleculer call math.add --@a 5 --@b 3

Emit

--help Show help [boolean]

--config, -c Load configuration from a file [string] [default: ""]

--id NodeID [string] [default: null]

--serializer Serializer [string] [default: null]

Example with params & meta

moleculer emit math.add --transporter NATS --@id 3 --@name John --#meta-key MyMetaValue

Features

Try it in your browser!

Base Adapter
Moleculer’s default adapter is based on Use it to quickly set up and test you prototype.

const broker = new ServiceBroker();

// Create a DB service for `user` entities
broker.createService({
name: "users",

// Create a new user
.then(() => broker.call("users.create", {
username: "john",
name: "John Doe",
status: 1
}))

// Get all users
.then(() => broker.call("users.find").then(console.log));

Settings

All DB adapters share a common set of settings:

DB adapters also implement CRUD operations. These actions are published methods and can be called by other

services.

fields Array.<String>- Fieldsfilter.

limit Number required Max count of rows.

query Object required Query object. Passes to adapter.

Type: Array.<Object> - List of found entities.

searchFields String required Fields list for searching.

query Object required Query object. Passes to adapter.

populate Array.<String>- Populated fields.

fields Array.<String> - Fields filter.

searchFields String required Fields for searching.

query Object required Query object. Passes to adapter.

Property

Type Default Description

- -

-

Property Type Default Description

entity Object - Entity to save.

Property Type Default Description

id any, Array.<any> required ID(s) of entity.

update

Update an entity by ID.

No input parameters.

Type: Object - Updated entity.

DB adapters also has a set of helper

Get entity(ies) by ID(s).

Property

Type Default Description

- -

-

Type: any

Decode ID of entity.

Find entities by query.

Property Type Default Description

sort String required Sorted fields.

search String required Search text.

_count

Get count of entities by query.

Type: Number

Count of found entities.

fields Array.<String>- Fieldsfilter.

page Number required Page number.

query Object required Query object. Passes to adapter.

Type: Object

Type: Object

entities Array.<Object> - Entities to save.

Type: Object, Array.<Object>

id any, Array.<any> required ID(s) of entity.

populate Array.<String> - Field list for populate.

Update an entity by ID.

After update, clear the cache & call lifecycle events.

Remove an entity by ID.

Property Type Default Description

You can easily use o modify (e.g. add timestamps, hash user’s passwords or remove sensitive info) before or after saving the data in DB.

Example of hooks adding a timestamp and removing sensitive data

// Load DB actions
mixins: [DbService],

// Add Hooks to DB actions
hooks: {
before: {
create: [
function addTimestamp(ctx) {
// Add timestamp
ctx.params.createdAt = new Date();
return ctx;
}
]
},
after: {
get: [
// Arrow function as a Hook
(ctx, res) => {
// Remove sensitive data
delete res.mail;
delete res.phoneNumber;

Example of populate schema

broker.createService({
name: "posts",
mixins: [DbService],
settings: {
populates: {
// Shorthand populate rule. Resolve the `voters` values with `users.get` action.

"reviewer": {
field: "reviewerId",
action: "users.get",
params: {
fields: "username fullName"
}
},

// Custom populator handler function
"rate"(ids, items, rule, ctx) {
// items argument is a mutable array containing response items
// the resolved value from promise do not matter - items array will always be passed as populated response
// so you should du custom populate by mutating items, if confused check implementaion:
// https://github.com/moleculerjs/moleculer-db/blob/master/packages/moleculer-db/src/index.js#L636

} }
});

Lifecycle entity events

There are 3 lifecycle entity events which are called when entities are manipulated.

entityUpdated(json, ctx) {
// You can also access to Context
this.logger.info(`Entity updated by '${ctx.meta.user.name}' user!`);
},

entityRemoved(json, ctx) {
this.logger.info("Entity removed", json);
},
});

module.exports = {
name: "posts",
mixins: [DbService],

settings: {
fields: ["_id", "title", "content", "votes"]
},

$ npm install moleculer-db moleculer-db-adapter-mongo --save

Dependencies

// Create a Mongoose service for `post` entities
broker.createService({
name: "posts",
mixins: [DbService],
adapter: new MongoDBAdapter("mongodb://localhost/moleculer-demo"), collection: "posts"
});

broker.start()
// Create a new post
.then(() => broker.call("posts.create", {
title: "My first post",
content: "Lorem ipsum...",
votes: 0
}))

Example with connection URI & options

new MongoDBAdapter( keepAlive: 1

Mongoose Adapter

This adapter is based on

const { ServiceBroker } = require("moleculer");
const DbService = require("moleculer-db");
const MongooseAdapter = require("moleculer-db-adapter-mongoose"); const mongoose = require("mongoose");

const broker = new ServiceBroker();

pass: process.env.MONGO_PASSWORD

keepAlive: true

SQL adapter (Postgres, MySQL, SQLite & MSSQL) for Moleculer DB service with Sequelize.

$ npm install moleculer-db-adapter-sequelize --save

# For MSSQL
$ npm install tedious --save

"use strict";

broker.start()
// Create a new post
.then(() => broker.call("posts.create", {
title: "My first post",
content: "Lorem ipsum...",
votes: 0
}))

// Get all posts
.then(() => broker.call("posts.find").then(console.log));

);

Example with connection options

min: 0,

});

Copyright © 2009-2023 UrgentHomework.com, All right reserved.