centress v0.2.3
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:
Name | Type | Description |
---|---|---|
init | function | Use to initialize the module. |
routes | function | Use to add page routes for the module. |
api | function | Use to add API endpoints for the module. |
index | number | Index 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
Path | Type | Env | Default | Description |
---|---|---|---|---|
apiBaseUrl | string | /api | Base URL for API routes in modules | |
logLevel | string | APP_LOG_LEVEL | all | Log4js log level |
server.host | string | APP_HOST | localhost | Host for Express server |
server.port | number | APP_PORT or PORT | 3000 | Port for Express server |
paths.root | string | boot caller dir | Absolute path to root directory | |
paths.modules | string | <root>/modules | Absolute Path to own local custom modules without using package.json | |
paths.moduleConfigs | string | <root>/config | Absolute Path to individual module config | |
express.settings | object | {} | Key/value pair for Express settings. | |
log4js.appenders | object | {console, file} | Log4js appenders config. Do not replace the whole object. | |
log4js.categories | object | {default, file} | Log4js categories config. Do not replace the whole object. | |
log4js.pm2 | boolean | true | Log4js use PM2 |
Read Only
Path | Type | Description |
---|---|---|
production | boolean | true if NODE_ENV === 'production' otherwise false . Aliased with prod . |
development | boolean | Negation 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.
Config | Type | Default | Description |
---|---|---|---|
index | number | -9999 | Loading index (should be first in line) |
helmet | boolean | true | Enable/disable helmet |
helmetConfig | object | null | Helmet's config |
json | boolean | true | Parse application/json body |
urlencoded | boolean | true | Parse application/x-www-form-urlencoded body |
centress-health
Handles GET /health
, responding 200 OK
immediately.
Config | Type | Default | Description |
---|---|---|---|
index | number | -9998 | After centress-parser |
centress-mongoose
Responsible for connecting to MongoDB database using Mongoose. Environment variables are used, instead of default, if present.
Config | Type | Env | Default | Description |
---|---|---|---|---|
index | number | -9997 | After centress-health | |
user | string | APP_DB_USER | '' | |
password | string | APP_DB_PASS | '' | |
database | string | APP_DB_NAME | '' | Database name |
host | string | APP_DB_HOST | localhost | |
port | number | APP_DB_PORT | 27017 | |
validateOnUpdate | boolean | true | Run validation on update | |
options | object | { useNewUrlParser: true } |
centress-response
Last in middleware/module chain. Flush payloads written by res.data()
. Also responsible for defining static files serving.
Config | Type | Default | Description |
---|---|---|---|
index | integer | NaN | Should be last in line |
staticDirs | array (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