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);
};
},
};
};
@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