interlayer v0.13.7
interlayer
At this point, the server version is still in alpha. You can suggest an idea or report an error here: New issue
The stable version of the server will be implemented after writing all the necessary features and tests to them.
Changelog
Features
- Serving Static Content
- Upload files
- Auto-reload server on file change (reload on new files not supported)
- Clusterization
- Postgres\mysql\redis built-in DAL's for data storage
- Mailgun\sparkpost\smtp build-in mail sender packages
- Localization
- WebScoket
Install
npm install --save interlayeror
yarn add interlayerProject tree example
- /node_modules/
- package.json
- /files/
- this folder will be served by config.serve- index.html
- style.css
- script.js
- /images/
- logo.jpeg
- index.js
- /i18n/
- en-US.json
- /middleware/
- auth.js
- /modules/
- weather.js
- config.json
Request processing steps
- request preparation - filling with functions and objects, see here
- search for a module. if not found - search for a file in
config.serveand give it to the client - if the module is found, parse the data if the request method is POST
- for all matching triggers run middlewares, more see here
- if the found module has a prerun in its meta, run it, more see here
- finally launch our module(that can use any data from here), in response we wait for see here
- convert to json text if meta.toJson || meta.contentType == 'json' || headers'Content-Type' == 'application/json'
- done
/modules/weather.js example
exports._obtain = {
checkIp: true,
prerun(request, moduleMeta, cb){
}
};
exports.obtain = (request, cb){
cb(null, 'good weather');
};/middleware/auth.js example
exports.triggers = {
'meta.checkIp': (request, moduleMeta, cb) => {
if(request.ip === '127.0.0.1'){
return cb('bad ip');
}
cb();
},
'request.params.city': (request, moduleMeta, cb) => {
if(request.params.city === 'london'){
return cb('bad weather');
}
cb();
}
}Variants to start server
const config = {
port: 80,
serve: ['files']
};
require('interlayer')(config);or
require('interlayer')('config.json');or
const server = require('interlayer').server();
server
.setRootPath(__dirname)
.loadConfigFile('config.json')
.start()config object or config.json file configuration
Avaliable params:
Avaliable properties in config object or config.json file
| Property | Default | Type | Description |
|---|---|---|---|
port | 8080 | Number | Web server port number |
secure | --- | Object | SSL configuration object with paths to files: {key:'',cert:''} |
initPath | ./ | String | Web server root path |
logPath | ./ | String | Path to create the logs.log file |
timeout | 60(sec) | Number | Timeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted |
workers | 1 | Number | Number of instances for load balancing. If number more than 1, uses node.js cluster |
restartOnChange | false | Boolean | Flag determine is server will restart automatically when files in the folder with modules was changed |
useDals | --- | ObjectdalName = dalConfig | The configuration object for dal modules to be used. Supports redis(redis:{}), mysql(mysql:{} and default will be {host: '127.0.0.1',user: 'root'}), postgress(postgress:{} and default will be {host: '127.0.0.1',user: 'root'}). For config built-in redis see here🌍, mysql see here🌍, postgres see here🌍 (Example of dal's config: useDals: {mysql: {port: 6375, user: 'admin'}, redis: {}}) |
useEmailSenders | --- | ObjectemailSenderName = emailSenderConfig | The configuration object for the mail senders to be used. Supports mailgun(mailgun:{}), sparkpost(sparkpost:{}), smtp(smtp:{}). For config built-in mailgun see here🌍, sparkpost see here🌍, smtp see here🌍 |
serve | --- | Array[Strings[]] | An array folders to serve. Priority over the last folder |
modules | ./modules | Array[Strings[]] | An array folders with modules. Priority over the last folder. (Default directory is './modules' unless otherwise specified.) How to create |
views | ./files | Array[Strings[]] | An array of folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.) |
i18n | ./i18n | Array[Strings[]] | An array of folders with localization files. Priority over the last folder. (Default directory is './i18n' unless otherwise specified.) How to create |
dals | --- | Array[Strings[]] | An array of folders with your dal(Data Access Layer) modules. Priority over the last folder. How to create |
emailSenders | --- | Array[Strings[]] | An array of folders with your email senders. Priority over the last folder. How to create |
middleware | --- | Array[Strings[]] | An array of folders with middlewares. Priority over the last folder. How to create |
middlewareOrder | --- | Array[Strings[]] | An array with ordered names of middlewares |
middlewareTimeout | 10(sec) | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted |
skipDbWarning | false | Boolean | Skip warning in console if useDals not defined in config |
defaultHeaders | --- | ObjectheaderName = headerValue | An object with default headers, which have to be added to the every response |
debug | false | Boolean | Allow to display log.d in console and add to the logs.log file |
instantShutdownDelay | 1500(ms) | Number | Delay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown |
retryAter | 10(sec) | Number | Time in seconds for Retry-After response header with server HTTP 503 status. Works until instantShutdownDelay |
noDelay | true | Boolean | Flag to enable/disable Nagle algoritm for all connections. See here🌍 |
websocket | --- | Boolean/Object | Start websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket |
useHttpErrorFiles | false | Boolean | Possible errors will be given as files if they are found in directories specified in addViewPath |
skipParsePost | false | Boolean | Skip parse POST |
formidableOptions | {} | Object | Set formidable options on parse data when headers'content-type' not null, list of options see here🌍 |
startInits | true | Boolean | Start functions that were added via Module app.setInit |
disableLogFile | false | boolean | Disable to write log file, the console.log and others continues to be written by the console |
Intrlayer instance configure
let serverInstance = require('interlayer').server();How to use
let server = require('interlayer').server();
server.setRootPath(__dirname);
server.loadConfigFile('config.json');
server.start();Avaliable methods:
| Property | Default | Type | Description |
|---|---|---|---|
start(configObject / null) | --- | Object | Starting the server with/without the configuration object |
loadConfigFile(path) | --- | String | Initializing configuration from file |
setConfig(configObject) | --- | Object | Setting a configuration from an object |
getConfig(configObject) | --- | Object | Get the resulting configuration |
setRootPath(path) | ./ | String | Set root directory |
setLogPath(path) | ./ | String | Set a directory of the log file |
setPort(port) | 8080 | Number | Set the server port |
setSecure(secureObject) | --- | Object | SSL configuration object with paths to files: {key:'',cert:''} |
setWorkersCount(workerNumber) | 1 | Number | Number of instances for load balancing. If number more than 1, uses node.js cluster |
setTimeout(timeout) | 60(sec) | Number | Timeout in seconds, then user will see {error: 'TIMEOUT'} Note, execution of the method is not interrupted |
setDefaultHeaders(headersObject) | --- | Object | An object with default headers, which have to be added to the every response |
setRestartOnChange([true / false]) | false | Boolean | Boolean value determine is server will restart automatically when files in the folder with modules was changed |
setSkipDbWarning([true / false]) | false | Boolean | Skip warning in console if useDals not defined in config |
setDebugMode([true / false]) | false | Boolean | Allow to display log.d in console |
setNoDelay([true / false]) | true | Boolean | Flag to disable/enable Nagle algoritm for all connections. See here🌍 |
setInstantShutdownDelay(timeout) | 1500(ms) | Number | Delay in milliseconds after server will shutdown on process SIGINT or SIGTERM signal, or process message: shutdown |
setRetryAter(timeout) | 10(sec) | Number | Time in seconds for Retry-After response header with server HTTP 503 status. Works until config.instantShutdownDelay |
addEmailSender(emailSenderName, emailSenderConfig) | --- | String, Object | Add an email sender. Priority over the last folder. How to create |
addDalPath(path, [path, [path]]) | --- | String | Add path to DAL's(Data Access Layer) modules. Priority over the last added path |
addDal(dalName, dalConfig) | --- | String, Object | The configuration(dalConfig) for dal module(dalName) to be used. Out of the box is available redis(for use specify redis, {}), mysql(for use specify mysql, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}), postgress(for use specify postgress, {} and default dalConfig will be {host: '127.0.0.1',user: 'root'}). For configure redis see here🌍, mysql see here🌍, postgres see here🌍 |
addMiddlewarePath(path, [path, [path]]) | --- | String, ... | Add path to middleware modules. Priority over the last added path. How to create |
setMiddlewareOrder(middlwareName, middlwareName) | --- | String or ArrayStrings | An array(or arguments) with ordered names of middlewares |
setMiddlewareTimeout(timeout) | 10(sec) | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted |
addModulesPath(path, [path, [path]]) | ./modules | String, ... | Add path to modules. Priority over the last added path. (Default directory is './modules' unless otherwise specified.) How to create |
addI18nPath(path, [path, [path]]) | ./i18n | String, ... | Add path to localization files. Priority over the last added path. (Default directory is './i18n' unless otherwise specified.) How to create |
addServePath(path, [path, [path]]) | --- | String, ... | Add path to Serving Static Content. Priority over the last added path |
addViewPath(path, [path, [path]]) | ./files | String, ... | Folders with files, which you can be uses as templates, or returned through the api(by using request.getView). Priority over the last folder. (Default directory is './files' unless otherwise specified.) |
setWebsocketConfig(websocket) | --- | Boolean/Object | Start websocket. If true then on the same port as server, except as stated in the Object. See here🌍. Initialized server instance can be found in initFunction simpleRequest.websocket |
setUseFilesAsHTTPErrors([true / false]) | false | Boolean | Possible errors will be given as files if they are found in directories specified in addViewPath |
setSkipParsePost([true / false]) | false | Boolean | Set skip parse POST |
setFormidableOptions({}) | {} | Object | Set formidable options on parse data when headers'content-type' not null, list of options see here🌍 |
disableInits([true / false]) | true | Start functions that were added via Module app.setInit | |
disableLogFile([true / false]) | false | boolean | Disable to write log file, the console.log and others continues to be written by the console |
Module creation
Example of modules/myModule.js
const app = require('interlayer').module();
exports.module = app;
let log = app.getLog('myModuleId');
app.setMeta({asJson: true});
app.setInit((request, requestCallback)=>{
log.i('Module inited');
requestCallback();
});
app.addMethod('myMethod', {toJson: true}, (request, requestCallback)=>{
let fullLog = request.modifyLog(log);
log.i('I am log without requestId but with myModuleId');
request.log.i('I am log with requestId but without myModuleId');
fullLog.i('I am log with requestId and with myModuleId');
requestCallback(null, {ok: true}, 200, {}, false);
});//Could be called in the path of /myModule/myMethodAvaliable app methods
const app = require('interlayer').module();| Method | Property types | Description |
|---|---|---|
getLog(name) | String | Get the object to output messages to the console. Object of {i:funcion, e:function, d: function, w: function, c: function} type |
setMeta(metaObject) | Object | Set the default parameters for all methods of this module. metaObject |
setInit(initFunction) | Function | Set the function to initialize the module at server start. initFunction |
addMethod(methodUrl, [methodMeta,] methodFunction) | String, Object, Function | Adds a new method with/without info(meta). methodMeta and methodFunction |
add(methodUrl, [methodMeta,] methodFunction) | String, Object, Function | Alias for addMethod |
setMethodInfo(methodUrl, methodMeta) | String, Object | Sets info(meta) for method. methodMeta |
info(methodUrl, methodMeta) | String, Object | Alias for setMethodInfo |
getMethod(methodUrl) | String | Returns the method function |
getMethodInfo(methodUrl, [withGlobalMeta]) | String, Boolean | Returns method info(meta) |
initFunction(simpleRequest)
simpleRequest.url- Empty stringsimpleRequest.headers- Empty objectsimpleRequest.DAL- DAL objects if initialisedsimpleRequest.config- Configuration objectsimpleRequest.websocket- websocket server instanse if initialised
... and functions as in methodFunction request except getResponse, getRequest and other http request methods See here🌍
metaObject and methodMeta default paramerets
| Key | Type | Description |
|---|---|---|
default = methodFunction | Function | Module(not method) function, can be used to output HTML, Available at /moduleUrl. Only for metaObject. methodFunction |
html = methodFunction | Function | Same as default. Only for metaObject |
find = methodFunction | Function | The method search function is only triggered if no other methods have been processed. Only for metaObject. See methodFunction |
path | String | Changes methodUrl to path |
addToRoot | Boolean | Skip moduleUrl and use methodUrl or path as url to method |
alias | String | Alias path to method |
timeout | Number | Seconds until HTTP 408(Request timeout) |
noDelay | Boolean | Disable/enable the use of Nagle's algorithm. See here🌍 |
middlewareTimeout | Number | Timeout in second, then user will see {error: 'TIMEOUT'} Note, execution of the runned middlewares is not interrupted |
prerun = prerunFunction | Function | Function or link to function which will be runned before method. May be usefull for preparing request. prerunFunction |
toJson | Boolean | Convert response to JSON string |
contentType | String | Same as toJson if contentType==json |
skipRequestLog | String | Skip request log output |
hidden | Boolean | Skip method from return information while calling request.getMethodsInfo |
skipParsePost | Boolean | Skip parse POST |
prerunFunction(request, moduleMeta, requestCallback)
request- same as in methodFunction
methodFunction(request, requestCallback):
request:
| Methods | Property types | Description |
| --- | --- | --- |
| modifyLog(log) | Object | Add to log object requestId for log created with global.logger |
| getView(file, callback) | String, Function | Return file in callback from paths specified in config.views or in server.setViewPath(). callback = (error, data) |
| getViewSync(file) | String | Synchronous request.getView |
| getFile(file, callback) | String, Function | Return file as is. callback = (error, data, {'Content-type':''}) |
| addCookies(key, value) | String, String | Set coockie for response |
| rmCookies(key) | String | Remove coockie from responce |
| i18n(key[, defaultValue]) | String, String | Return translate for key or return defaultValue|
| obtainI18n() | --- | Return object with languages |
| getMethodsInfo(showHidden) | Boolean | Returns all methods (except hidden methods if showHidden is not specified) |
| lockShutdown() | --- | Blocks the termination of the process until the request is completed |
| unlockShutdown() | --- | Unlock the termination of the process |
| getResponse() | --- | Returns the original responce |
| getRequest() | --- | Returns the original request |
| error(text) | String | Returns 503 http code |
| end(text[, code[, headers[, type]]]) | String[, Number[, Object, String]] | Returns code http code with text(as binary if type==bin) and headers|
request:
| Property | Type | Description |
| --- | --- | --- |
| config | Object | An object of configuration specified at start of server |
| ip | String | Client ip adress |
| url | String | Request url |
| path | String | Request path(module/method) |
| method | String | Uppercased type of request - POST|GET|... |
| isPost | Boolean | true|false |
| params | Object | An object of parsed GET params |
| post | Object | An object of parsed POST params(with formidable🌍) |
| files | Object | An object of uploaded files(with formidable🌍) |
| cookies | Object | An object of parsed cookies |
| headers | Object | An object of request headers |
| DAL | Object | An object with DALs, which was initialized by config.useDals or server.addDal() |
| mail | Object | An object with mail senders, which was initialized by config.useEmails or server.addEmailSender() |
| id | String | requestId |
| log | Object | The same as global.logger.create(moduleID), but with requestID included(not include moduleID) |
| helpers | Object | requiest.helpers |
request.helpers
| Methods | Property types | Description | |
|---|---|---|---|
helpers.generateId() | --- | Geneate 8-character identifier(a-zA-Z0-9) | |
helpers.toJson(obj) | * | Convert obj to JSON string | |
helpers.clearObj(obj, toRemove) | Object, Array | Delete parameters of obj from toRemove array of strings | |
helpers.isBoolean(val) | * | Check is val string is Boolean(true | false) |
helpers.JSV(json, schema, envId) | Object, Object, String | See here🌍. Create environment with envId and call validate with json and schema | |
helpers.mime() | Object | return mime type by file extension or fallback or 'application/octet-stream' |
requestCallback(error, data, httpCode, responseHeaders, isBinary)
error- null or undefined or String or Objectdata- null or String or Object or Binary(ifisBinary= true)httpCode- null or Number See here🌍responseHeaders- null or Object See here🌍 If Content-Type = application/json thendatawill be returned as JSONtype- null or 'bin'. If 'bin' thendatawill be returned as Buffer
Global objects added
global.logger
Object to create log with global.logger.create(logName) or global.logger(logName) were logName is String.
log avaliable methods:
| Methods | Description |
| --- | --- |
| i | Parameters same as for console.log. See here🌍 |
| w | Parameters same as for console.warn. See here🌍 |
| e | Parameters same as for console.error. See here🌍 |
| d | Parameters same as for console.debug. See here🌍 |
| c | Parameters same as for console.log. See here |
Note that this type of logging don't allow to track the request id.
To have ability track the request id use the request.modifyLog method:
let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
let log = request.modifyLog(log);
}global.intervals
Object with methods:
| Methods | Property types | Description |
| --- | --- | --- |
| add(function, timeout) | Function, Number | Return key |
| add(function, timeout = {year: '*', month: '*', date: '*', day: '*', hour: '*', minute: '*', second: '*'}) | Function, Object | Any of the parameters can be "*" or "2020,2040" - the options are listed in commas. Return key |
| del(key) | String | Remove by key |
| disable(key, flag) | String, Boolean | Disable/Enable interval by key and flag |
| enable(key) | String | Enable interval by key |
The startup interval is every second. If the start conditions (timeout) match, function is called with a parameter as a function to delete the interval - similar call to global.intervals.del(key).
Often used inside the function initFunction
Remember, if at server startup config.startInits = false or disableInits(false) then functions added via setInit(initFunction) will not be added to global.intervals
Features
Logging:
const log = global.logger('moduleID');
log.i(); // Usual log - displayed in green
log.w(); // Warn - displayed in yellow
log.e(); // Error - displayed in red
log.c(); // Critical error - displayed in whiteNote that this type of logging don't allow to track the request id.
To have ability track the request id use the request.modifyLog method:
const log = global.logger('moduleID');
exports.myMethod = (request, cb)=>{
let log = request.modifyLog(log);
}Or use the request.log instead, if 'moduleID'(identifier specified in global.logger.create function) not required.
let log = global.logger.create('moduleID');
exports.myMethod = (request, cb)=>{
let log = request.log;
}
---Use dals:
request.DAL.redis.get('somekey', (err, data) => {
if(err){
request.log.e('redis.get somekey', err);
return cb(err);
}
...
});
request.DAL.mysql.query('SELECT * FROM users WHERE login = ?', ['admin'], (err, data, fields) => {
if(err){
request.log.e('mysql.query', err);
return cb(err);
}
})Use email senders
request.mail.mailgun.send({}, callback) -> see params here https://documentation.mailgun.com/api-sending.html#sending
request.mail.sparkpost.send({}, callback) -> see params here https://developers.sparkpost.com/api/transmissions.html#header-transmission-attributes
//or use initialized senders as you want
request.mail.mailgun.client -> https://www.npmjs.com/package/mailgun-js
request.mail.sparkpost.client -> https://www.npmjs.com/package/sparkpostCreate dal
Example of dals/nameofdal.js
Then you can add nameofdal to config.useDals array (ex: config.useDals = {nameofdal: {...config}};)
// init is not required
exports.init = (config, dalConfig) => {
};
// but methods is required
exports.methods = {
get: () => {},
set: () => {}
}Create email sender
Example of emailSenders/nameofsender.js
Then you can add nameofsender to config.useEmailSenders array (ex: config.useEmailSenders = {nameofsender: {...config}};)
// init is required
exports.init = (config, emailConfig) => {
};
// send is required
exports.send = (email, cb)=>{
}Create middleware
Example of middleware/session.js
exports.triggers = {
'meta.checkSessions': 'checkSession', // you can specified string - name of function exported in module
'request.params.test': exports.test // also you can specified function or method of object
};
// but methods is required
// request context and callback described in module
exports.checkSession = (request, moduleMeta, cb) => {
};
exports.test = (request, moduleMeta, cb) => {
};or
exports.run = (request, moduleMeta, cb)=>{
};
// this creates
// exports.triggers = {
// '*': exports.run
// };Localization
Example of i18n/en.js
Note! You have to use double quotes, instead single quotes, because it's json file These are the actual keys used for error output.
{
"Not found": "Nothing found, 404, Bill Gates site",
"<center>Error 404<br>Not found</center>": "<h1>404 HTTP error.<h1>Not found</center>",
"Service Unavailable. Try again another time.": "503 HTTP error."
}1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago