Skip to content

Instantly share code, notes, and snippets.

@icebob
Created January 10, 2019 20:14
Show Gist options
  • Save icebob/c0bce54436379d29c1bee8521ceb5348 to your computer and use it in GitHub Desktop.
Save icebob/c0bce54436379d29c1bee8521ceb5348 to your computer and use it in GitHub Desktop.
Inter-namespace middleware

Inter-namespace middleware

This middleware can connect to other namespaces.

Usage

Connect to other namespaces with the same broker options

moleculer.config.js

const InterNamespaceMiddleware = require("./inter-namespace.js");

module.exports {
    namespace: "local",
    nodeID: "node-1",
    transporter: "NATS",
    middlewares: [
        InterNamespaceMiddleware(["ns-mars", "ns-venus"])
    ]
}

or with different broker options

moleculer.config.js

const InterNamespaceMiddleware = require("./inter-namespace.js");

module.exports {
    namespace: "local",
    nodeID: "node-1",
    transporter: "NATS",
    middlewares: [
        InterNamespaceMiddleware([
            { 
                namespace: "ns-mars",
                transporter: "NATS"
            }, 
            {
                namespace: "ns-venus",
                transporter: "Redis",
            }
        ])
    ]
}

Calling services

// Call service in the local namespace
broker.call("greeter.hello");

// Call service in the local namespace with the namespace
broker.call("greeter.hello@local");

// Call service in the "ns-venus" namespace
broker.call("greeter.hello@ns-venus");

// Call service in the "ns-mars" namespace
broker.call("greeter.hello@ns-mars");
const { ServiceBroker } = require("moleculer");
const _ = require("lodash");
module.exports = function InterNamespaceMiddleware(opts) {
if (!Array.isArray(opts))
throw new Error("Must be an Array");
let thisBroker;
const brokers = {};
return {
created(broker) {
thisBroker = broker;
opts.forEach(nsOpts => {
if (_.isString(nsOpts)) {
nsOpts = {
namespace: nsOpts
};
}
const ns = nsOpts.namespace;
this.logger.info(`Create internamespace broker for '${ns} namespace...'`);
const brokerOpts = _.defaultsDeep({}, nsOpts, { nodeID: null, middlewares: null }, broker.options);
brokers[ns] = new ServiceBroker(brokerOpts);
});
},
started() {
return Promise.all(Object.values(brokers).map(b => b.start()));
},
stopped() {
return Promise.all(Object.values(brokers).map(b => b.stop()));
},
call(next) {
return function(actionName, params, opts = {}) {
if (_.isString(actionName) && actionName.includes("@")) {
const [action, namespace] = actionName.split("@");
if (brokers[namespace]) {
return brokers[namespace].call(action, params, opts);
} else if (namespace === thisBroker.namespace) {
return next(action, params, opts);
} else {
throw new Error("Unknow namespace: " + namespace);
}
}
return next(actionName, params, opts);
};
},
};
};
@kevyworks
Copy link

I should take note however that if namespace: "ns-mars", transporter: "NATS" the nodes transporter should also be "NATS", else it wont see it.

@noob3lite
Copy link

noob3lite commented Oct 20, 2019

This middleware does not work with moleculer Api gateway.
Parameter actionName is an Object of Type Endpoit
opt is always undefined.
My plan was to extend callOptions in Routedefinition to achive something like a multitenant Gateway, but the Gateway Service somehow messes this up. Any idea how to solve this?

@jellydn
Copy link

jellydn commented Apr 5, 2022

I have implemented a bit of adjustment for support load services per namespace just in case someone needs it like me :).

import _ from 'lodash';
import { ServiceBroker, BrokerOptions, Middleware } from 'moleculer';

import { logger } from './logger';

export default function interNamespaceMiddleware(
  opts: Array<{ brokerOptions: BrokerOptions; servicesPath?: string[] }>,
): string | Middleware {
  if (!Array.isArray(opts)) throw new Error('Must be an Array');

  let thisBroker: ServiceBroker;
  const brokers: Record<string, ServiceBroker> = {};

  return {
    created(broker: ServiceBroker) {
      thisBroker = broker;
      opts.forEach((nsOpts) => {
        const ns = nsOpts?.brokerOptions.namespace ?? 'N/A';
        logger.warn(`Create inter namespace broker for '${ns} namespace...'`);
        const brokerOpts = _.defaultsDeep(
          {},
          nsOpts.brokerOptions,
          {
            nodeID: `${ns}-api-${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,
          },
          broker.options,
        ) as BrokerOptions;
        const brokerService = new ServiceBroker(brokerOpts);

        nsOpts.servicesPath?.forEach((path) => brokerService.loadService(path));

        brokers[ns] = brokerService;
      });
    },

    async started() {
      logger.warn('Start inter namespace broker...');
      return Promise.all(Object.values(brokers).map(async (b) => b.start()));
    },

    async stopped() {
      logger.warn('Stop inter namespace broker...');
      return Promise.all(Object.values(brokers).map(async (b) => b.stop()));
    },

    call(next: (actionName: string, params: any, options: Record<string, unknown>) => any) {
      return function (actionName: string, params: any, options = {}) {
        logger.warn('Call inter namespace broker...', actionName, params, options);
        if (_.isString(actionName) && actionName.includes('@')) {
          const [action, namespace] = actionName.split('@');

          if (brokers[namespace]) {
            return brokers[namespace].call(action, params, options);
          }

          if (namespace === thisBroker.namespace) {
            return next(action, params, options);
          }

          throw new Error(`Unknown namespace: ${namespace}`);
        }

        return next(actionName, params, options);
      };
    },
  };
}

@xiopt
Copy link

xiopt commented May 23, 2022

@jellydn nice addition. I think you should add middlewares: null to brokerOpts else I think you will get an infinite broker creation loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment