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.
- 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
npm install --save interlayer
or
yarn add interlayer
- /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 preparation - filling with functions and objects, see here
- search for a module. if not found - search for a file in
config.serve
and 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
exports._obtain = {
checkIp: true,
prerun(request, moduleMeta, cb){
}
};
exports.obtain = (request, cb){
cb(null, 'good weather');
};
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();
}
}
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()
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 |
let serverInstance = require('interlayer').server();
let server = require('interlayer').server();
server.setRootPath(__dirname);
server.loadConfigFile('config.json');
server.start();
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 |
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
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) |
-
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🌍
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 |
-
request
- same as in methodFunction
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 |
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' |
-
error
- null or undefined or String or Object -
data
- 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 thendata
will be returned as JSON -
type
- null or 'bin'. If 'bin' thendata
will be returned as Buffer
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);
}
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
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;
}
---
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);
}
})
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
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: () => {}
}
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)=>{
}
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
// };
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."
}