8.0.0 • Published 7 years ago

mservice v8.0.0

Weekly downloads
16
License
MIT
Repository
github
Last release
7 years ago

Microservice core

Greenkeeper badge

Build Status Code Climate semantic-release Commitizen friendly

This module provides boilerplate for microservice core and a few plugins for starters. It sets up convenient connect and close methods, logging features, as well as input validation. At the same time it is an event emitter and may send log and other events silently.

Migration from 2.x to 3.x

Version 3 bring a neat feature of supporting multiple transports and request lifecycle. Please consult releases page on how to migrate your code

Usage

Extend Mservice class, populate plugins with array of their names. Currently supported:

  • amqp
  • cassandra
  • elasticsearch
  • http
  • logger
  • redisCluster
  • redisSentinel
  • router
  • socketIO
  • validator

Events:

  1. ready - when all plugins are up
  2. close - when all plugins were disconnected
  3. plugin:connect:pluginName, instance
  4. plugin:close:pluginName
  5. error, err - on critical error

Example

const path = require('path');
const Mservice = require('mservice');
const ld = require('lodash');

class UserService extends Mservice {

  /**
   * default options
   * @type {Object}
   */
  static defaultOpts = {
    plugins: ['validator', 'logger', 'amqp', 'redisCluster'],
    redis: {
      hosts: [{
        host: 'localhost',
        port: 6379
      }],
      options: {
        keyPrefix: 'nice'
      }
    },
    amqp: {
      transport: {
        queue: 'roundrobin',
      },
    },
    logger: {
      defaultLogger: true,
    },
    // relative paths will be resolved relatively to the first dir of the file
    // on the call stack that is not src/plugins/validator.js or src/index.js
    // keep that in mind when creating instances of services
    //
    // if that's tricky - pass absolute paths!
    validator: [ '../schemas' ],
  }

  constructor(opts = {}) {
    super(ld.merge({}, UserService.defaultOpts, opts));
  }
}

const userService = new UserService();
// methods that userService will have are explain below

Methods

initPlugin(mod, conf)

Initializes plugin, which has 2 methods: .attach - it would be called with service as context and conf as first arg When conf is omitted - it looks for mod.name - make sure this is also exported. .attach can return connect and close functions, which must return promises for starting and stopping the plugin

hook(event, ...args)

Performs Promise.map listeners defined for event. All of them are called with the context of the mservice and args are applied as a spread. This is useful when you want to track custom event hooks completion in the app.

Constructor accepts hooks Object, which contains of a map of event to a function or array of functions. They could either be sync or a promise.

Plugins

Validator plugin

When using this plugin - make sure you npm i ms-validation -S

Attaches ms-validation instance to your class on ._validator. Exposes .validate and .validateSync methods on the class itself. Pass array of absolute and relative paths when creating service to automatically include your schemas. They will be available under basename of the file. If names collide - last schemas will overwrite existing ones

// MixedData - any variable to be checked
userService.validate('schemaName', MixedData)
  .then(mixedData => {
    // passed validation
  })
  .catch(err => {
    // validation failed
  })

const validationResult = userService.validateSync('schemaName');
if (validationResult.error) {
  // validation failed
  // handle error
}

// resulting doc if filter: true was set, otherwise original doc
validationResult.doc

Logger plugin

When using this plugin - make sure you npm i bunyan -S

Attaches .log method, which is an instance of a bunyan logger. Provides sane defaults when NODE_ENV is set to development. If not includes ringBuffer trace logger with 100 records. Can accept either a boolean value or an existing custom bunyan instance;

logger config

  • defaultLogger - when options is set to true - will output to stdout, when to false - only to ringBuffer.
  • debug - when debug is on - default log level is debug, otherwise - info.
  • name - logger name, will have name of service._config.name or mservice if not set.
  • streams - steams config, keys are name of stream, values are stream config

Predefined steams

  • sentry
logger: {
  streams: {
    stream: {
      dns: 'sentry-dns',
      level: 'error',
      options: {
        // sentry options
      },
    },
  },
}

Example

const userService = new UserService({
  logger: {
    debug: false,
    defaultLogger: true
  }
});

// will output data to stdout
userService.log.info('Flying just fine!');

// will only save to ringBuffer stream
userService.log.debug('You won\'t see me!');

AMQP plugin

When using this plugin, make sure you also do npm i ms-amqp-transport -S

Enables AMQP transport makeomatic/ms-amqp-transport It allows the service to communicate over AMQP protocol. If service.router plugin is enabled, then we will make the best attempt to setup listeners and route incoming messages through this plugin. Attaches ._amqp to service.

Events are emitted when plugin has completed connecting, or disconnecting. First arg is the transport instance

  1. plugin:connect:amqp
  2. plugin:close:amqp
const userService = new UserService({
  amqp: {
    transport: {
      queue: 'my-nice-queue',
      listen: ['users.ping'],
    },
    router: {
      enabled: true
    },
  }
});

// messages that are sent to users.ping will be processed

RedisCluster plugin

NOTE: you can use only 1 of the plugins for redis - either cluster or sentinel

When using this plugin, make sure you also do npm i ioredis -S

Enables redisCluster communication based on ioredis module. Allows one to setup connection to redis and communicate with it;

Events are emitted when plugin has completed connecting, or disconnecting. First arg is the transport instance

  1. plugin:connect:redisCluster
  2. plugin:close:redisCluster
const userService = new UserService({
  plugins: [ 'redisCluster' ],
  redis: {
    hosts: [{
      host: '...',
      port: Number
    }],
    options: {
      // ...
    }
  }
});

// any redis command will be applicable

Redis Sentinel plugin

NOTE: you can use only 1 of the plugins for redis - either cluster or sentinel

When using this plugin, make sure you also do npm i ioredis -S

Enables redisCluster communication based on ioredis module. Allows one to setup connection to redis and communicate with it in a highly available fashion;

Events are emitted when plugin has completed connecting, or disconnecting. First arg is the transport instance

  1. plugin:connect:redisSentinel
  2. plugin:close:redisSentinel
const userService = new UserService({
  plugins: [ 'redisSentinel' ],
  redis: {
    sentinels: [{
      host: '...',
      port: Number
    }],
    name: 'mservice',
    options: {
      // ...
    }
  }
});

Elasticsearch plugin

When using this plugin, make sure you also do npm i elasticsearch -S

Enables to use Elasticsearch as a NoSQL storage/search engine. Wraps an official Elasticsearch JavaScript API module.

Events are emitted when plugin has completed connecting, or disconnecting. First arg is the transport instance

  1. plugin:connect:elasticsearch
  2. plugin:close:elasticsearch
const userService = new UserService({
  plugins: [ 'elasticsearch' ],
  elasticsearch: {
    host: 'example.elastic.search:9200',
    apiVersion: '2.1',
    //...
  }
});

Cassandra plugin

When using this plugin, make sure you also do npm i express-cassandra -S

Enables to use Cassandra as a NoSQL storage/search engine. Based on express-cassandra module.

Events are emitted when plugin has completed connecting, or disconnecting. First arg is the transport instance

  1. plugin:connect:cassandra
  2. plugin:close:cassandra
cassandra = require('express-cassandra');

const service = new Service({
  plugins: [ 'cassandra' ],
  cassandra: {
    service: {
      // models also can be path to directory with models
      // https://github.com/masumsoft/express-cassandra#write-a-model-named-personmodeljs-inside-models-directory
      models: {
        Foo: {
          fields:{
            bar: 'text'
          },
          key:['bar']
        }
      }
    },
    client: {
      clientOptions: {
        contactPoints: ['cassandra.srv'],
        protocolOptions: {
          port: 9042
        },
        keyspace: 'mykeyspace',
        queryOptions: {
          consistency: cassandra.consistencies.one
        }
      },
      ormOptions: {
        defaultReplicationStrategy : {
          class: 'SimpleStrategy',
          replication_factor: 1
        },
        dropTableOnSchemaChange: false,
        createKeyspace: true
      }
    }
});

Http plugin

Features

  • Allows creating http server
  • Predefined handlers support

Handlers

You can use one of predefined handlers in /src/plugins/http/handlers directory

Allowed handlers at this moment:

  • express (make sure you also do npm i express -S)
  • restify (make sure you also do npm i restify -S)
  • hapi (make sure you also do npm i hapi -S, also additional dependencies may be required, for example if you want to use some of hapi's plugins)

Peer dependencies

  • npm i server-destroy -S

Events

  • plugin:start:http
  • plugin:stop:http

Usage

const service = new Service({
  plugins: [ 'http' ],
  http: {
    server: {
      attachSocketIO: false, // if true socketio plugin need to be included
      handler: 'restify',
      handlerConfig: {},
      port: 3000,
    }
  }
});
Hapi features

That's possible to use hapi plugins such as vision, bell, etc. All you need is extend the config of http server. Possible options are:

const service = new Service({
  plugins: [ 'http' ],
  http: {
    server: {
      handler: 'hapi',
      handlerConfig: {
        /** implicitly loads 'vision' plugin and decorates a native hapi request with 'sendView' method
         *  https://github.com/hapijs/vision
         */
        views: {
          engines: {
            hbs: require('handlebars'),
          },
          paths: 'path/to/templates',
          relativeTo: __dirname,
        },
        plugins: {
          list: [{
            // you can provide a plugin name, but be sure you've included it in the dependencies
            register: 'bell',
            options: {
              /** bell options */
            }
          }, {
            // also you can provide a function as a plugin
            register: require('path/to/custom/plugin'),
            options: {
              /** options */
            }
          }, {
            // or even just a path to the file
            register: 'path/to/custom/plugin',
            options: {}
          }],
          options: {
            /** https://hapijs.com/api#plugins */
          }
        }
      },
      port: 3000,
    }
  }
});

// next in route handler you can use a native hapi request instance. 
/** file: some/action/handler.js */
module.exports = function actionHandler(request) {
  const context = {
    example: true,
  };

  return request.transportRequest.sendView('view', context);
}

// decorates a native hapi request with 'redirect' method.
/** file: redirect/action/handler.js */
module.exports = function redirectHandler(request) {
  return request.transportRequest.redirect('https://github.com/makeomatic');
}

Socket.io plugin

Features

Attach Socket.io instance to .socketIO property.

Config

  • router
    • enabled - boolean, enable router, default false
  • options - object, socket.io options
    • adapter - object, adapter
      • name - string, adapter name, e.g. amqp
      • options - object, adapter options

Peer dependencies

  • npm i socket.io -S
  • npm i ms-socket.io-adapter-amqp -S

Usage

const service = new Service({
  plugins: [ 'socketio' ],
  socketio: {
    router: {
      enabled: true,
    },
    options: {
      // socket.io options
    },
  }
});

// service.socketIO - Socket.io instance

Router plugin

Attach router to service that can be used by other plugins

8.0.0

7 years ago

7.1.0

7 years ago

7.0.0

7 years ago

6.1.1

7 years ago

6.1.0

7 years ago

6.0.1

7 years ago

6.0.0

7 years ago

5.1.0

7 years ago

5.0.1

7 years ago

5.0.0

7 years ago

4.9.0

7 years ago

4.8.0

7 years ago

4.7.4

7 years ago

4.7.3

7 years ago

4.7.2

7 years ago

4.7.1

7 years ago

4.7.0

7 years ago

4.6.0

7 years ago

4.5.1

7 years ago

4.5.0

7 years ago

4.4.0

8 years ago

4.3.2

8 years ago

4.3.1

8 years ago

4.3.0

8 years ago

4.2.0

8 years ago

4.1.0

8 years ago

4.0.2

8 years ago

4.0.1

8 years ago

4.0.0

8 years ago

3.7.1

8 years ago

3.7.0

8 years ago

3.6.0

8 years ago

3.5.2

8 years ago

3.5.1

8 years ago

3.5.0

8 years ago

3.4.0

8 years ago

3.3.1

8 years ago

3.3.0

8 years ago

3.2.0

8 years ago

3.1.1

8 years ago

3.1.0

8 years ago

3.0.2

8 years ago

3.0.1

8 years ago

3.0.0

8 years ago

2.6.0

8 years ago

2.5.1

8 years ago

2.5.0

8 years ago

2.4.0

8 years ago

2.3.0

8 years ago

2.2.0

8 years ago

2.1.1

8 years ago

2.1.0

8 years ago

2.0.0

8 years ago

1.7.1

8 years ago

1.7.0

8 years ago

1.6.0

8 years ago

1.5.0

8 years ago

1.4.0

8 years ago

1.3.0

8 years ago

1.2.3

8 years ago

1.2.2

8 years ago

1.2.1

8 years ago

1.2.0

8 years ago

1.1.0

8 years ago

1.0.0

8 years ago

0.13.2

8 years ago

0.13.1

8 years ago

0.13.0

8 years ago

0.12.0

8 years ago

0.11.4

8 years ago

0.11.3

8 years ago

0.11.2

8 years ago

0.10.1

8 years ago

0.10.0

8 years ago

0.9.0

8 years ago

0.1.0

8 years ago