
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
moleculer-sc
Advanced tools
An API Gateway service for Moleculer framework using SocketCluster
What is SocketCluster? SocketCluster is an open source real-time framework for Node.js. It supports both direct client-server communication and group communication via pub/sub channels. It is designed to easily scale to any number of processes/hosts and is ideal for building chat systems.
socket.authToken
=> moleculer ctx.meta.user
)$ npm install moleculer-sc
SocketCluster is a fast, highly scalable HTTP + WebSockets server environment which lets you build multi-process real-time systems that make use of all CPU cores on a machine/instance.
Before you start, you have to create a SocketCluster project, and write the code in worker.js
. (See /examples/simple
)
Create your own SocketCluster Gateway service.
// SocketCluster worker.js
const SCWorker = require('socketcluster/scworker');
const { ServiceBroker } = require('moleculer')
const SocketClusterService = require('moleculer-sc')
class Worker extends SCWorker {
run() {
console.log(' >> Worker PID:', process.pid);
var environment = this.options.environment;
let broker = new ServiceBroker({
logger: console
})
broker.createService({
name:'sc-gw',
mixins:[SCService(this)], //create SCService with worker.
})
broker.start()
}
}
By default, moleculer-sc
will handle the call
event which proxy to moleculer's broker.call
Examples:
test.hello
action: socket.emit('call',{action:'test.hello'}, callback)
math.add
action with params: socket.emit('call',{action:'math.add', params:{a:25, b:13}}, callback)
socket.emit('call',{ action: '$node.health' }, callback)
socket.emit('call', { action: '$node.list'}, callback)
If you don’t want to public all actions, you can filter them with whitelist option. You can use match strings or regexp in list.
broker.createService({
name:'sc-gw', // SocketCluster GateWay
mixins:[SocketClusterService(worker)],
settings: {
routes: [{
event: "call",
whitelist: [
// Access to any actions in 'posts' service
"posts.*",
// Access to call only the `users.list` action
"users.list",
// Access to any actions in 'math' service
/^math\.\w+$/
]
}]
}
})
You can create multiple routes with different prefix, whitelist, alias, calling options & authorization.
broker.createService({
mixins: [SocketClusterService(worker)],
settings: {
routes: [
{
event: "adminCall",
whitelist: [
"$node.*",
"users.*",
]
},
{
event: "call",
whitelist: [
"posts.*",
"math.*",
]
}
]
}
});
You can implement authorization. For this you need to do 2 things.
getMeta
method of sc-gw
service. (Optional)Example authorization:
socket.on('login', function (credentials, respond) {
broker.call('v1.account.login', data).then(res=>{
socket.setAuthToken(res) //success
callback(null,res)
}).catch(err=>{
const errObj = _.pick(err, ["name", "message", "code", "type", "data"])
callback(errObj)
}) //error
})
For convenience, we did this for you. You could set a handler with login
type:
broker.createService({
mixins: [SocketClusterService(worker)],
settings: {
routes: [
{
event: "login",
type:'login', //Set route type to login, when calling success, you are logged in!
whitelist: [
"login.password",
"login.google",
"login.github"
]
}
]
}
});
// Add an handler service
broker.createService({
name:'login',
actions: {
password(ctx) {
if(ctx.params.user == 'tiaod' && ctx.params.password == 'pass'){
return {id: 'tiaod'}
}else{
throw new Error('UNAUTH')
}
}
}
})
Then:
socket.emit('login', {action:'login.password', params: {user: 'tiaod', password:'pass'}}, function(err, data){
console.log('call login.passoword:',data)
console.log(socket.authToken.id == 'tiaod') //true
})
Also you could overwrite the getMeta
method to add more addition meta info. The default getMeta
method is:
getMeta(socket){
return {
user: socket.authToken
}
}
Example to add more additional info:
broker.createService({
name:'sc-gw',
mixins:[SocketClusterService(worker)],
methods:{
getMeta(socket){ //construct the meta object.
return {
user: socket.authToken,
socketId: socket.id
}
}
}
})
The route has a callOptions property which is passed to broker.call. So you can set timeout, retryCount or fallbackResponse options for routes.
broker.createService({
mixins: [SocketClusterService(worker)],
settings: {
routes: [{
event:'call',
whitelist: [
"posts.*",
"math.*",
],
callOptions: {
timeout: 500,
retryCount: 0,
fallbackResponse(ctx, err) { ... }
}
}]
}
});
Note: If you provie a meta field here, it replace the getMeta
method's result.
broker.createService({
mixins: [SocketClusterService(worker)],
settings: {
routes: [{
event:'call',
callOptions: {
meta: { abc:123 }
}
}]
},
methods:{
getMeta(socket){
return {
user: socket.authToken
} //This will be replaced by callOptions.meta
}
}
});
If you want to do a role-based access control, you can do it on SocketCluster way. Here is an example using node_acl
:
let acl = require('acl')
acl = new acl(new acl.memoryBackend())
acl.allow('admin', 'math', 'add') // allow admin to call
acl.addUserRoles('user id here', 'admin')
scServer.addMiddleware(scServer.MIDDLEWARE_EMIT,
async function (req, next) {
if(!data || typeof data.action !== 'string') next(new Error('invaild request'))
let [service, action] = req.data.action.split('.', 2)
if (!await acl.isAllowed(req.socket.authToken.id, service, action)) {
next(); // Allow
} else {
var err = MyCustomEmitFailedError(req.socket.id + ' is not allowed to call action ' + req.event);
next(err); // Block
// next(true); // Passing true to next() blocks quietly (without raising a warning on the server-side)
}
}
);
You can rewrite global-level error handler:
In handler, you must call the
respond
. Otherwise, the request is unhandled.
broker.createService({
mixins: [SocketClusterService(worker)],
settings: {
routes: [{
event:'call',
callOptions: {
meta: { abc:123 }
}
}]
},
methods:{
onError(err, respond){ //This is the default handler
const errObj = _.pick(err, ["name", "message", "code", "type", "data"]);
return respond(errObj)
}
}
});
You can also use SocketCluster as moleculer's transporter!
// worker.js
const { ServiceBroker } = require('moleculer')
const SCTransporter = require('moleculer-sc/transporter')
let broker = new ServiceBroker({
nodeID: "node-1",
logger: console,
transporter: new SCTransporter({
exchange:this.exchange
})
})
broker.createService({
name:'math',
actions: {
add(ctx) {
return Number(ctx.params.a) + Number(ctx.params.b);
}
}
})
broker.start().then(()=>{
console.log('broker1 started!')
})
// external-service.js
const { ServiceBroker } = require('moleculer')
const SCTransporter = require('moleculer-sc/transporter')
let broker2 = new ServiceBroker({
nodeID: "node-2",
logger: console,
transporter: new SCTransporter({
hostname:'localhost',
port:8000
})
})
broker2.start()
.then(() => broker2.call("math.add", { a: 5, b: 3 }))
.then(res => console.log("5 + 3 =", res))
Warning: You should add a SocketCluster middleware to apply access control with MOL.
channel prefix.
You can also pass an socket object or SCExchange instance:
new SCTransporter({
socket:socket
})
// or
new SCTransporter({
exchange:exchange
})
Just do:
let data = {
hello: 'world'
}
broker.call('sc-gw.publish',{
topic: 'your.topic.here',
data: data
})
0.9.0 - Add publish
action.
0.8.1 - Fix getMeta
error.
0.8.0 - Add login
handler type.
0.7.0 - Add onError
handler
0.6.1 - You can pass socket
or exchange
object to SCTransporter now.
0.6.0 - Breaking change: Don't pass worker in settings anymore, you should pass the worker when initerlized the service.
// old:
broker.createService({
name:'sc-gw', // SocketCluster GateWay
mixins:[SCService], //This will not work anymore
settings:{
worker,
}
})
// new:
broker.createService({
name:'sc-gw',
mixins:[SCService(worker)], //create SCService with worker.
})
This is because the settings is also obtainable on remote nodes, it is transferred during service discovering, which will cause a TypeError: Converting circular structure to JSON
when serializing it.
0.5.0 - Add transporter.
0.4.0 - Add multiple routes support.
0.3.0 - Doesn't integrate node_acl
anymore. If you need access control lists, you can do it on socketcluster side.
FAQs
API Gateway service for Moleculer framework using SocketCluster
The npm package moleculer-sc receives a total of 19 weekly downloads. As such, moleculer-sc popularity was classified as not popular.
We found that moleculer-sc demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.