@comodinx/microservice
v0.1.2
Published
@comodinx/microservice is a Node.js microservice base helpers.
Downloads
11
Readme
@comodinx/microservice
Node.js microservice base helpers.
Index
Download & Install
NPM
npm i @comodinx/microservice
Source code
git clone https://gitlab.com/comodinx/microservice.git
cd microservice
npm i
How is it used?
NOTE: Based on bluebird promises.
Getting started
const pkg = require('./package.json');
const { app, listen } = require('@comodinx/microservice/server');
const { errors, health } = require('@comodinx/microservice/server/routes');
app.get('/', (req, res) => res.send('Hello World!'));
// Handle route for health check
health(app, { pkg /* , router, database, services: ['other-service-name'] */ });
// Handle all errors (400, 404, 500, etc...)
errors(app);
listen(app);
/*
* Listen routes
* + GET / Home
* + GET /health Health check
* + ANY /* 404 error. {code: 404, error: 'Not Found'}
*/
Configure
For app/server.
| Environment variable | Values | Type | Default value | Description |
|:-------------------------------|:------------------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------|
| SERVER_HOST | host name | string | undefined | Host name where the server listens |
| SERVER_PORT / PORT | port number | number | 8000
| Port where the server listens |
| SERVER_MULTIPART | true/false | boolean | false
| Indicate if app use support for multipart middleware |
| SERVER_HEALTH_PATH | health check path | string | "/health"
| Route for health check endpoint |
| BODY_PARSER_LIMIT | request limit | string | "5mb"
| Indicate request size limit (generic, apply to raw, text, json and urlencoded) |
| BODY_PARSER_RAW_LIMIT | request limit for raw | string | "5mb"
| Indicate request size limit for raw |
| BODY_PARSER_TEXT_LIMIT | request limit for text | string | "5mb"
| Indicate request size limit for text |
| BODY_PARSER_JSON_LIMIT | request limit for json | string | "5mb"
| Indicate request size limit for json |
| BODY_PARSER_URLENCODED_LIMIT | request limit for url encoded | string | "5mb"
| Indicate request size limit for url encoded |
For database
Simple configuration
| Environment variable | Values | Type | Default value | Description |
|:-------------------------------|:----------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------|
| DB_ENABLED | true/false | boolean | true
| Indicate if need support for database |
| DB_NAME | database name | string | null | |
| DB_HOST | database host name | string | 'localhost'
| |
| DB_PORT | database port | number | 3306
| |
| DB_USER | database user | string | 'username'
| |
| DB_PASS | database password | string | null | |
| DB_TIMEZONE | database timezone | string | '+00:00'
| |
Read/Write replication configuration
| Environment variable | Values | Type | Default value | Description |
|:-------------------------------|:----------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------|
| DB_HOST_WRITE | database host name | string | DB_HOST
| Database host name for write replication |
| DB_PORT_WRITE | database port | number | DB_PORT
| Database port for write replication |
| DB_USER_WRITE | database user | string | DB_USER
| Database username for write replication |
| DB_PASS_WRITE | database password | string | DB_PORT
| Database password for write replication |
| DB_HOST_READ | database host name | string | DB_HOST
| Database host name for read replication |
| DB_PORT_READ | database port | number | DB_PORT
| Database port for read replication |
| DB_USER_READ | database user | string | DB_USER
| Database username for read replication |
| DB_PASS_READ | database password | string | DB_PORT
| Database password for read replication |
Pool configuration
| Environment variable | Values | Type | Default value | Description |
|:-------------------------------|:----------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------|
| DB_POOL_ENABLED | true/false | boolean | true
| Indicate if need support for db pool connections |
| DB_POOL_MAX | max pool connections | number | 10 | Maximum number of connection in pool |
| DB_POOL_MIN | min pool connections | number | 0 | Minimum number of connection in pool |
| DB_POOL_IDLE | max idle time | number | 10000 | Maximum time, in milliseconds, that a connection can be idle before being released |
| DB_POOL_ACQUIRE | max time reconnect | number | 60000 | Maximum time, in milliseconds, that pool will try to get connection before throwing error |
| DB_POOL_EVICT | interval time | number | 1000 | Time interval, in milliseconds, after which sequelize-pool will remove idle connections |
For services
| Environment variable | Values | Type | Default value | Description | |:-------------------------------|:----------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------| | SERVICE_AGENT | service agent name | string | '' | | | SERVICE_CONNECTION_TIMEOUT | number in miliseconds | number | 21000 | | | SERVICE_REQUEST_TIMEOUT | number in miliseconds | number | 20000 | |
For global dependencies
| Environment variable | Values | Type | Default value | Description |
|:-------------------------------|:----------------------|:--------|:------------------------|:-------------------------------------------------------------------------------------------|
| DEFAULT_LOCALE | locale | string | 'es'
| Default locale |
| MOMENT_LOCALE | locale | string | DEFAULT_LOCALE
| Default locale for moment module |
| MOMENT_DEFAULT_FORMAT | date format | string | 'YYYY-MM-DD HH:mm:ss'
| Default format for moment.format |
| NUMERAL_LOCALE | locale | string | DEFAULT_LOCALE
| Default locale for numeral module |
| NUMERAL_DEFAULT_INTEGER_FORMAT | number format | string | '0,0'
| Example usage numeral(1000).format(numeral.defaultIntegerFormat)
|
| NUMERAL_DEFAULT_DECIMAL_FORMAT | number format | string | '0,0.00'
| Example usage numeral(10.01).format(numeral.defaultDecimalFormat)
|
| NUMERAL_DEFAULT_BALANCE_FORMAT | number format | string | '$ 0,0.00'
| Example usage numeral(100.2).format(numeral.defaultBalanceFormat)
|
| LODASH_TRUNCATE_LENGTH | length | number | 24
| Default truncate length use for lodash. Example usage _.truncate('hi-diddly-ho there');
|
| LODASH_TRUNCATE_SEPARATOR | string separator | string | ' '
| Default truncate separatotr use for lodash. Example usage _.truncate('hi diddly there');
|
Detailed explanation
Server
const {
// Base express application instance
// Example usage:
// app.get('/', (req, res) => res.send('Hello World!'));
app,
// Listen express application instance.
// If the `config('server.https.enabled')` is false, listen from the express application instance will be executed.
// If the `config('server.https.enabled')` is true, listen from the instance of https.createServer({ key, cert }) will be executed.
// Example usage:
// listen(app);
//
// NOTE: Listen does nothing when NODE_ENV=test
listen
} = require('@comodinx/microservice/server');
Routes
const {
// Routes for error handling. Handle all responses errors (400, 404, 500, etc...)
errors,
// Route for health check.
health
} = require('@comodinx/microservice/server/routes');
Examples for errors
Simple usage
errors(app);
Examples for health
Simple usage
health(app);
Complex usage
health(/* express appplication or express.Router */ app, {
// To be able to change the url of health check.
url: '/health',
// To be able to import package.json and tag.json if they exist. <process.cwd()>
rootDir: null,
// For add package information details on health response. (name, description and version) <require('./package.json')>
pkg: null,
// For add tag details on health response. <{commit, version, name}>
tag: null,
// IDEM url. Default `url`
path: null,
// In order to run the health check on a specific router (express.Router).
router: null,
// For add database information details on health response. (is alive, error). <database instance require('@comodinx/sequelize')>
database: null,
// For add required services information details on health response. (is alive, error) <string list of services added to @comodinx/microservice/helpers/service>
service: null
});
Get by ID
Get model by id
const Route = require('@comodinx/microservice/server/routes/getById');
const route = new Route('YourModel', {
// Indicates the name of the parameter to search in `req.params`. Default 'id'
paramNameId: 'id',
// Indicates the validation that will be applied to the parameter. Default 'isInteger'
// Possible values:
// Function
// Example:
// function (id, req) { return id > 10; }
// String (https://www.gitlab.com/comodinx/microservice#validator)
validatorIdType: 'isInteger'
});
module.exports = route.handlerize();
Destroy
Delete model. (**WARNING** - Physical erasure! If req.query.force = true
)
const Route = require('@comodinx/microservice/server/routes/destroy');
const route = new Route('YourModel', {
// Indicates the name of the parameter to search in `req.params`. Default 'id'
paramNameId: 'id',
// Indicates the validation that will be applied to the parameter. Default 'isInteger'
// Possible values:
// Function
// Example:
// function (id, req) { return id > 10; }
// String (https://www.gitlab.com/comodinx/microservice#validator)
validatorIdType: 'isInteger'
});
module.exports = route.handlerize();
List
DEPRECATED
List (pagination, filter, order and group) from database model
const Route = require('@comodinx/microservice/server/routes/list');
const route = new Route('YourModel', {
// Indicates the page size. Default 10
defaultPageSize: 10,
// Indicates the validation that will be applied to the parameter. Default null
// Possible values:
// Object with key-value, where the key is the name of the parameter in the filter, and the value is the name of the column in the database
// Example:
// { idType: 'id_yourmodel_type' }
mapKeyParse: null
});
module.exports = route.handlerize();
List by query
List (pagination, filter, order and group) from database model
const Route = require('@comodinx/microservice/server/routes/listByQuery');
const route = new Route('User', {
// Relationship with others tables or services. Default value is null
relationships: [
// Example for related table
{
// relationship name
name: 'attributes',
// related table name
table: 'job_candidates',
// field needed from related table for join relationship
fieldSource: 'candidates.id_job',
// field needed from model for match join relationship
fieldTarget: 'jobs.id'
},
// Example for related with remote service
{
// relationship name
name: 'person',
// indicate that relationship is with a remote service
remote: 'true',
// service name. helpers/service[<service name>]
service: 'people',
// url of remote endpoint
uri: '/people',
// field needed from remote service for join relationship
fieldSource: 'id',
// field needed from model for match join relationship
fieldTarget: 'users.id_person'
}
],
// Indicates the page size. Default 10
defaultPageSize: 10,
// Indicates the validation that will be applied to the parameter. Default null
// Possible values:
// Object with key-value, where the key is the name of the parameter in the filter, and the value is the name of the column in the database
// Example:
// { idType: 'id_yourmodel_type' }
mapKeyParse: null
});
module.exports = route.handlerize();
Proxy
Handrails between microservices
const Route = require('@comodinx/microservice/server/routes/proxy');
const route = new Route({
// Indicates the service name defined on @comodinx/microservice/helpers/service.
serviceName: 'myService',
// Indicates the function use on service instance. Default `req.method`
serviceMethod: req.method,
// Indicates the path use when calling service. Default `req.path`
serviceUrl: req.path,
// Extra service request options. Default `{}`
serviceOptions: {
responseType: 'json'
},
// Indicate if response with the same service response content type.
sameContentType: true
// TODO :: Send in service request, the same header from original request
// headers: [
// 'authorization'
// ]
});
module.exports = route.handlerize();
Base route
const _ = require('lodash');
const Base = require('@comodinx/microservice/server/routes');
const { validator } = require('@comodinx/microservice/helpers');
const { errors, database } = require('@comodinx/microservice');
// ...
class Route extends Base {
/**
* Validate request
*/
validate (req) {
if (_.isEmpty(req.params)) {
throw new errors.BadRequest('Bad request. Please set "id" parameter');
}
if (!validator.isInteger(req.params.id)) {
throw new errors.BadRequest('Bad request. Please use "id" on parameters');
}
return req;
}
/**
* Handle request
*/
handler (req) {
return database.models.YourModel.getById(req.params.id).then(model => {
if (!model) {
throw new errors.NotFound(`Not found model ${context.id}`);
}
return model;
});
}
}
module.exports = (new Route()).handlerize();
Config
In order to see all config documentation click here
const { config } = require('@comodinx/microservice');
// ...
// Read configuration
config('server.port'); // 8000
// Extend current configuration.
config.extend({ server: { port: 8001 } });
config('server.port'); // 8001
// Use default value.
config('server.port2', 8001); // 8001
Database
In order to see all database documentation click here
database instance
const { errors, database } = require('@comodinx/microservice');
// ...
return database.models.YourModel.getById(id).then(yourModel => {
if (!yourModel) {
throw new errors.NotFound(`Model not found ${id}`);
}
return yourModel;
});
Example use raw database, function query
In order to see all operators click here
const { database } = require('@comodinx/microservice');
// ...
return database.models.YourModel.find({
where: {
deleted_at: {
[database.Op.is]: null
}
},
order: [['id', 'DESC']],
limit: 100,
offset: 0
});
Example use raw database, function query
In order to see all query types click here
const { database } = require('@comodinx/microservice');
// ...
return database.query('UPDATE users SET active = 0 WHERE id = 1', { type: database.QueryTypes.UPDATE }).spread((results, metadata) => {
// ...
return metadata;
});
database model
Bear in mind that everything has an order. Extending the models (for now) requires an order in the
require
In the first file of our app / microservice, we have to do the require in the following order
// Setting require('./config'); // Dependencies require('@comodinx/microservice/helpers/dependencies'); // Our model extensions require('./models'); ...
Example add extra functionality on model.
File path models/organizations.js
File source:
const { database, database: { Model } } = require('@comodinx/microservice');
class Organizations extends Model {
/*
* Function used for populate relationships when model if loaded.
*/
populate (organization, options) {
options = options || {};
if ((options.tiny && !options.with) || options.small) {
return super.populate(organization, options);
}
return super.populate(organization, options)
// Load users, from remote users service, related with the organization.
.then(() => this.populateRemotes(organization, 'users', options));
}
/*
* Another extra function
*/
findAllActives () {
// ...
}
}
// Export model Organizations, and extend model organizations on database instance.
module.exports = database.models.Organizations = Organizations;
Usage model extension:
const { database } = require('@comodinx/microservice');
database.models.Organizations.findAllActives().then(models => ...);
Errors
const { errors } = require('@comodinx/microservice');
// ...
throw new errors.NotFound();
// All HTTP Status Code are an Exception
const e = new errors.NotFound('My custom message', {hello: 'world'});
// ...
console.log(e.toJson());
// { "error": 'My custom message', "code": 404, "extra": { "hello": "world" } }
Validator
In order to see all validation functions click here
const validator = require('@comodinx/microservice/helpers/validator');
validator.isEmail('[email protected]'); // true
Custom validations.
Integer
validator.isInteger(); // false
validator.isInteger(null); // false
validator.isInteger('a'); // false
validator.isInteger('6'); // true
validator.isInteger('6.1'); // false
validator.isInteger('-6'); // true
validator.isInteger(6); // true
validator.isInteger(6.1); // false
validator.isInteger(-6); // true
Number
validator.isNumber(); // false
validator.isNumber(null); // false
validator.isNumber('a'); // false
validator.isNumber('6'); // true
validator.isNumber('6.1'); // true
validator.isNumber('-6'); // true
validator.isNumber(6); // true
validator.isNumber(6.1); // true
validator.isNumber(-6); // true
Logger
const logger = require('@comodinx/microservice/helpers/logger');
logger.error(new Error('Not Found')); // [2020-01-28T17:16:50.379Z] - ERROR - ✘ Ooops... Error: Not Found
logger.error('This is an error'); // [2020-01-28T17:16:50.379Z] - ERROR - ✘ Ooops... This is an error
logger.warn('This is a warning'); // [2020-01-28T17:16:50.381Z] - WARN - ⚠ This is a warning
logger.info('Hello World!'); // [2020-01-28T17:16:50.381Z] - INFO - Hello World!
logger.title('Hello World!'); // [2020-01-28T17:16:50.382Z] - INFO - ========== Hello World! ==========
logger.success('Hello World!'); // [2020-01-28T17:16:50.383Z] - INFO - ✔ Hello World!
logger.arrow('Hello World!'); // [2020-01-28T17:16:50.384Z] - INFO - • Hello World!
logger.step('Hello World!'); // [2020-01-28T17:16:50.384Z] - INFO - ✦ Hello World!
logger.lap('Hello World!'); // [2020-01-28T17:16:50.384Z] - INFO - ➜ Hello World!
Tests
In order to see more concrete examples, I INVITE YOU TO LOOK AT THE TESTS :)
Run the unit tests
npm test