0.2.3 • Published 6 years ago

centress v0.2.3

Weekly downloads
4
License
MIT
Repository
github
Last release
6 years ago

Centress

A tiny modular monolith Express framework for faster development and maintainable web application.

Features

  • :white_check_mark:   Modular design
  • :white_check_mark:   Plug and play modules
  • :white_check_mark:   Area or feature as a module
  • :white_check_mark:   Dependency injection
  • :white_check_mark:   Zero setup / configuration
  • :white_check_mark:   Easily migrate to microservices
  • :white_check_mark:   Scalable and extensible

Installation

npm install centress

Usage

Create main file index.js next to package.json and append the following code.

const centress = require('centress')
// Start centress server
centress.boot();

Run with node index.js and the server should be up and running. When you visit localhost:3000, you'll be greeted with an error Cannot GET / :rage:  ... but don't panic! That means it's working and home page is not handled yet.

Using Ready-Made Modules

One objective of Centress is to allow external ready-made module of features or areas in your application to be installed by npm. I've published a simple centress module called centress-hello on npm that handles / homepage automatically. Install it then restart server.

npm install centress-hello

Restart the server and you'll be greeted now with Hello World on home page.

You can always use different Centress modules from npm for each different areas/features in your application, or just create your own custom Centress module with your domain logic.

Centress Module

A Centress module is just a normal node module but is automatically detected and use its functions and features after installing. Should be at least no setup or configuration to use it.

Creating A Centress Module

In order for Centress to detect that your module is a Centress module, write centress.module(exports, options) like in the following code.

const centress = require('centress');
// Making this module a Centress module
centress.module(exports, {});
// Other export props and methods
exports.otherProps = 'value';
// ...

Though the above code tells that it's a Centress module, it doesn't really do anything yet.

.module(exports, options)

Give meaning to your module by defining options. Let's say the name of the following module is test in package.json file.

centress.module(exports, {
  
  // Initialize your module with dependecy injection
  init: main => {
    // Custom configuration provided by user
    main.config.foo ? 'Bar' : 'Baz';
    // Express root APP
    main.app.use(bodyparser());
    // Global default PAGE router (Express router) for all modules
    main.router.use(...);
    // Global API router (Express router) for all modules
    main.api.use(...);
    // Native node http object
    main.server
  },

  // Register PAGE routes (Express router).
  // The `pageRouter` is ony for this module,
  // and is different from above router in init method
  routes: pageRouter => {
    // http://localhost:3000/test/hello
    pageRouter.get('/hello', (req, res) => {
      res.send('Hello World');
    });
    // more routes ...
  },

  // Register API endpoint (Express router).
  // The `endpointRouter` is also only for this module
  api: endpointRouter => {
    // http://localhost:3000/api/test/hello
    endpointRouter.get('/hello', (req, res) => {
      res.json({ hello: 'world' });
    });
    // more endpoints ...
  }

  // Optional. Ordering index. Defaults to last in middleware chain
  index: 0
  // 0  means first in the chain

});

Options:

NameTypeDescription
initfunctionUse to initialize the module.
routesfunctionUse to add page routes for the module.
apifunctionUse to add API endpoints for the module.
indexnumberIndex order for the module. Indexes are used by Centress to sort the order of modules. The lower the number, the higher the priority.

Centress Object

.boot(rootPath)

Start the server and initialize all installed Centress module.

.set(path, value)

Set a configuration. See list below. Eg. centress.set('server.port', 8080).

.lib(name)

Require built-in Cenrtress libraries. Eg. centress.lib('logger/file').

.get(moduleName)

Require other Centress modules.

const userModule = centress.get('user-module');
const foo = userModule.getById('ABC123');

.module(exports, options)

Use to create a Centress module. See above for how to create a Centress module.

Configuration

Configuration must be set before calling centress.boot() method. Environment variables are used, instead of default, if present.

// Writing config
const centress = require('centress');
centress.set('server.port', 8080);
// more sets ...
centress.boot();

// Accessing config
const { config } = require('centress');
const isProd = config.production ? 'YES' : 'NO';

Writable

PathTypeEnvDefaultDescription
apiBaseUrlstring/apiBase URL for API routes in modules
logLevelstringAPP_LOG_LEVELallLog4js log level
server.hoststringAPP_HOSTlocalhostHost for Express server
server.portnumberAPP_PORT or PORT3000Port for Express server
paths.rootstringboot caller dirAbsolute path to root directory
paths.modulesstring<root>/modulesAbsolute Path to own local custom modules without using package.json
paths.moduleConfigsstring<root>/configAbsolute Path to individual module config
express.settingsobject{}Key/value pair for Express settings.
log4js.appendersobject{console, file}Log4js appenders config. Do not replace the whole object.
log4js.categoriesobject{default, file}Log4js categories config. Do not replace the whole object.
log4js.pm2booleantrueLog4js use PM2

Read Only

PathTypeDescription
productionbooleantrue if NODE_ENV === 'production' otherwise false. Aliased with prod.
developmentbooleanNegation for production. Aliased with dev.

Config Per Module

Sample config per module are shown in the following code and to disable a module set disabled: true. All modules will have config index, prefix, disabled and are reserved keys.

const centress = require('centress');
centress.set('modules', {
  // A module
  'centress-mongoose': {
    // Loading index for ordering
    index: 1,
    // This configs will be passed to the module
    database: 'dbname',
    user: 'foo',
    password: 'abc123'
  },
  // Another module
  'sample-module': {
    // Override URL prefix for Page routes and API endpoints
    prefix: '/sample',
    // Load this module after above module `centress-mongoose`
    index: 2,
    // Disable or enable the module
    disabled: true
  }
  // more config ...
});
centress.boot();

Per module config can also be set using files under /config directory like /config/<module_name>.js.

module.exports = {
  prefix: '/sample',
  index: 10,
  foo: 'bar'
};

Custom Local Modules

You can also create your own custom module without using or registering it to package.json. By default, Centress will use all modules inside modules directory which can also be changed by setting paths.modules configuration.

File Structure

project
│   main.js
│   package.json    
│
└── config
│   │   security.js
│   │   user.js
│   │   otherModule.js
│   |   ...
│
└── modules
│   └── security
│   │   └── controllers
│   │   │   crypto.js (private)
│   │   │   auth.js (private)
│   │   │   index.js (public centress module, expose methods here)
│   │   │   ...
│   │  
│   └── user
│   │   └── models
│   │   └── controllers
│   │   │   index.js
│   │   │   ...
│   │  
│   └── otherModule  
│   └── ...
└── otherFolder
└── ...

Built-in Modules

View all built-in modules here. All modules are enabled by default, to disable set disabled: true.

centress-parser

Does some request parsing and auditing.

ConfigTypeDefaultDescription
indexnumber-9999Loading index (should be first in line)
helmetbooleantrueEnable/disable helmet
helmetConfigobjectnullHelmet's config
jsonbooleantrueParse application/json body
urlencodedbooleantrueParse application/x-www-form-urlencoded body

centress-health

Handles GET /health, responding 200 OK immediately.

ConfigTypeDefaultDescription
indexnumber-9998After centress-parser

centress-mongoose

Responsible for connecting to MongoDB database using Mongoose. Environment variables are used, instead of default, if present.

ConfigTypeEnvDefaultDescription
indexnumber-9997After centress-health
userstringAPP_DB_USER''
passwordstringAPP_DB_PASS''
databasestringAPP_DB_NAME''Database name
hoststringAPP_DB_HOSTlocalhost
portnumberAPP_DB_PORT27017
validateOnUpdatebooleantrueRun validation on update
optionsobject{ useNewUrlParser: true }

centress-response

Last in middleware/module chain. Flush payloads written by res.data(). Also responsible for defining static files serving.

ConfigTypeDefaultDescription
indexintegerNaNShould be last in line
staticDirsarray (object)[]Array of public static directories
{
  staticDirs: [
    { path: '/static' },
    { path: '/public', options: { maxAge: 5000 } }
  ]
}

Extensions

res.success(payload, code = 'OK')

Immidiately send success JSON response with code and payload. For API endpoints only.

endpointRouter.get('/foo', (req, res) => {
  res.success({ foo: 'bar' });
});

// Response: 200
{ 
  success: true, 
  code: 'OK', 
  payload: { foo:'bar' } 
}

res.error(err, payload)

Immidiately send error JSON response with payload. For API endpoints only.

const centress = require('centress');
const HttpError = centress.lib('error/http');
endpointRouter.get('/foo_error', (req, res) => {
  res.error(
    new HttpError(HttpError.BAD_REQUEST, "Custom error message here"),
    { foo: "bar" }
  );
});

// Response: 400
{ 
  error: true, 
  code: 'BAD_REQUEST', 
  message: "Custom error message here", 
  payload: { foo: "bar" }
}

res.data(key, value)

Write payloads to response for centress-response to flush at the end of chain.

// In `user` module
endpointRouter.get('/foo', (req, res, next) => {
  res.data('user', { id: 'abc123', name: 'Foo'});
  next();
});

// In `post` module
endpointRouter.get('/foo', (req, res, next) => {
  res.data('posts', [{id: 'p1', title: 'Bar'}, {id: 'p2', title: 'Baz'}]);
  next();
});

// `centress-response` will flush the following payload
{
  success: true,
  code: 'OK',
  payload: {
    user: { id: 'abc123', name: 'Foo'},
    posts: [{id: 'p1', title: 'Bar'}, {id: 'p2', title: 'Baz'}]
  }
}

Libs

Require built-in libs using centress.lib('logger/file')

Errors

Check out all codes here. Available errors are error/http, error/internal.

const centress = require('centress');
const HttpError = centress.lib('error/http');
endpointRouter.get('/foo_error', (req, res) => {
  res.error(new HttpError(HttpError.BAD_REQUEST));
});

You can also define your custom error class.

const centress = require('centress');
const errorFactory = centress.lib('error');

// Key is custom error code and value is HTTP error code.
const codes = {
  'FOO': 400,
  'BAR_BAZ': 404,
  'YOO': 501
};

module.exports = errorFactory('Custom', codes);

Logger

Centress uses log4js but you can also ignore it and use your preferred logging library.

const centress = require('centress');
const fileLogger = centress.lib('logger/file');
endpointRouter.get('/foo', (req, res) => {
  fileLogger.info('Hello World!');
});

License

MIT