interlayer

0.13.3 • Public • Published

interlayer

npm version npm downloads github license Build Status Code Climate

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

CHANGELOG.md

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 interlayer

or

yarn add interlayer

Project 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

  1. request preparation - filling with functions and objects, see here
  2. search for a module. if not found - search for a file in config.serve and give it to the client
  3. if the module is found, parse the data if the request method is POST
  4. for all matching triggers run middlewares, more see here
  5. if the found module has a prerun in its meta, run it, more see here
  6. finally launch our module(that can use any data from here), in response we wait for see here
  7. convert to json text if meta.toJson || meta.contentType == 'json' || headers['Content-Type'] == 'application/json'
  8. 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 --- Object[dalName] = 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 --- Object[emailSenderName] = 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 --- Object[headerName] = 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 Array[Strings] 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/myMethod

Avaliable 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 string
  • simpleRequest.headers - Empty object
  • simpleRequest.DAL - DAL objects if initialised
  • simpleRequest.config - Configuration object
  • simpleRequest.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)

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
isPost Boolean true
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
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 Object
  • data - null or String or Object or Binary(if isBinary = true)
  • httpCode - null or Number See here🌍
  • responseHeaders - null or Object See here🌍 If Content-Type = application/json then data will be returned as JSON
  • type - null or 'bin'. If 'bin' then data will 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 white

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:

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/sparkpost

Create 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."
}

Package Sidebar

Install

npm i interlayer

Weekly Downloads

3

Version

0.13.3

License

Apache-2.0

Unpacked Size

154 kB

Total Files

33

Last publish

Collaborators

  • 8ai