escobar
v3.0.5
Published
Node.js micro framework for REST API or any other applications based on HTTP server. 0 dependencies. Fast, simple, flexible and light.
Downloads
27
Maintainers
Readme
Escobar
Node.js micro framework for REST API or any other applications based on HTTP server.
Application example with recommended architecture is here
Requirements
Node.js that supports async/await. (version 7.6 or higher has native support)
Features
- 0 dependencies
- Async/await
- Fast
- Simple
- Flexible
- Light
Navigation
Installation
npm i --save escobar
Quick start example
const EscobarServer = require('escobar');
const server = new EscobarServer();
server.host = '127.0.0.1';
server.port = 8080;
server.loadRoutes(__dirname + '/routes'); // Load routes from folder
server.startServer();
Documentation with examples
Escobar server
Settings
.host - Http server binding host. (Default: '0.0.0.0')
.port - Http server binding port. (Default: 3000)
.httpServer - Node.js http.Server (Available after .startServer()
exec`)
server.httpServer.timeout = 30000; // Set timeout to 30 sec.
.routes - Object with routes functions. (Default: {})
{
'/': [function],
'/some/endpoint': [function]
}
.useJsonParser - Parse request body with json, when Content-Type is 'application/json'. (Default: true)
.useMultipartParser - Parse request body (files and data), when Content-Type is 'multipart/form-data'. (Default: true)
.useUrlencodedParser - Parse request body data, when Content-Type is 'application/x-www-form-urlencoded'. (Default: true)
Events
All events functions must be async or return Promise.
Event: 'request'
Fires when we got new request.
Arguments:
server.on('request', async (requestData) => {
const res = requestData._response;
const req = requestData._request;
// Set response headers (default)
res.setHeader('Content-Type', 'application/json; charset=utf-8');
// Headers for cross domain requests
if (req.method == 'OPTIONS') {
res.setHeader('Allow', 'GET,POST,PUT,DELETE,OPTIONS');
}
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
// Or some stuff
// req.on('data', (chunk) => { console.log('request body chunk: ' + chunk) });
// req.on('end', () => {console.log("client request is complete. Let's send him response!")});
// If you set server.useJsonParser or server.useMultipartParser or server.useUrlencodedParser to false
// You can use your tools to parse this data
return true;
});
Event: 'before_endpoint'
Fires before routing function will be executed. If it return true
- routing function will be executed. If it return false
- routing function will NOT be executed.
Arguments:
server.on('before_endpoint', async (requestData) => {
// Get sessionId
requestData._sessionId =
requestData._request.headers.sessionid
|| requestData.$_GET.sessionId
|| requestData._http.getCookie(__sessionCookieName)
|| requestData._sessionId;
// Some function that check access
requestData._user = await auth(requestData);
});
Event: 'exec_route'
If this event is handled, you need to rewrite default execution. renderFunc - route function.
Arguments:
- requestData
- renderFunc
Default route execution is simply:
requestData._clientResponse = await renderFunc(requestData);
Custom execution example:
server.on('exec_route', async (requestData, renderFunc) => {
const method = requestData._request.method;
const funcToExec = renderFunc[method];
if (funcToExec) {
if (funcToExec.authOnly && !requestData._user) {
requestData._clientResponse = __unauthorized(requestData, "You don't have access to this resource.");
return false;
}
requestData._clientResponse = await funcToExec.exec(requestData);
} else {
requestData._clientResponse = __badRequest(requestData, `Method '${method}' is not supported for this endpoint`);
}
return true;
});
// Route index file
// ./routes/some/endpoint/__index.js
module.exports = {
GET: require('./GET'),
POST: require('./POST')
};
// Route logic file
// ./routes/some/endpoint/GET.js
module.exports = {
authOnly: true,
exec: async (requestData) => {
// Return response string that will be delivered to the client
return {
status: 'OK',
data: 'Hello world!'
};
}
};
Event: 'before_send_response'
Fires before we send response to client (response.end(requestData._clientResponse);
).
Arguments:
server.on('before_send_response', async (requestData) => {
// You can modify requestData._clientResponse here and it will be sent to client modified
try {
if (typeof requestData._clientResponse === 'object') {
requestData._clientResponse = JSON.stringify(requestData._clientResponse);
}
} catch (e) {
requestData._clientResponse = JSON.stringify({
status: "FAIL",
message: getErrorMsg(e)
})
}
});
Event: 'not_found'
Fires when we don't find any route for request. Example (https://example.com/endpoint/that/does/not/exists)
Arguments:
const endpointNotFound = JSON.stringify({
status: "FAIL",
message: "Endpoint not found"
});
server.on('not_found', async (requestData) => {
// Don't exec server.onBeforeSendResponse
// Because i don't want to waste CPU time for JSON.stringify every time
requestData._execOnBeforeSendResponse = false;
requestData._clientResponse = endpointNotFound;
return true;
});
Event: 'error'
Fires when we got error.
Arguments:
- requestData
- err
const getErrorMsg = (e) => {
let msg = 'Internal Server Error';
if (typeof e == 'string') msg = e;
if (typeof e == 'object') {
if (e.msg) msg = e.msg;
if (e.message) msg = e.message;
}
return msg;
};
server.on('error', async (requestData, err) => {
requestData._http.setCode(500);
requestData._clientResponse = {
status: 'FAIL',
message: getErrorMsg(err)
};
return true;
});
Events life cycle
- await util executed:
request
- await util executed:
before_endpoint
- await util executed:
exec_route
- await util executed:
before_send_response
Functions
.startServer() - Start server.
.loadRoutes(pathToFolder) - Load routes from folder. pathToFolder - Full path to folder that contains routes.
server.loadRoutes(__dirname + '/routesFolder');
How to use routes folder?
Here is example of files and folder structure:
routesFolder
__main
__index.js
someFolder
anotherFolder
__index.js
auth
__index.js
__index.js - entry point for route.
__main folder - route for '/'.
Result routes:
{
'/': [function],
'/someFolder/anotherFolder': [function],
'auth': [function]
}
requestData
requestData - is main object for manipulating your application.
requestData has a following properties by default for each request:
._request - Node.js http request from client.
._response - Node.js http response from server.
._route - Endpoint route (example: '/api/version'). (Default: false)
NOTE: If you define it inside .onRequest
callback, routing will try to navigate using this url, instead of url from request.
._routeParams - See explanation below. (Default: [])
Example 1:
We have:
Route with endpoint /api/user
Request with URL /api/user/1337/remove
In this case requestData._routeParams will be [1337, 'remove'].
Example 2:
Route with endpoint /api/user
Request with URL /api/user
In this case requestData._routeParams will be empty array [].
._clientResponse - This will be sent to client. (Default: '')
._execOnBeforeSendResponse - Do we need to exec callback onBeforeSendResponse
?. (Default: true)
._execRouting - Do we need to exec routing flow?. NOTE: Change this property available only inside onRequest
callback. (Default: true)
._execRoute - Do we need to exec route or 'exec_route' event?. (Default: true)
._customResponse - If true, response.end(requestData._clientResponse);
will not be executed in the end of request life cycle.
.$_DATA - Parsed data from request body. (Default: {})
.$_GET - Parsed data from query params. (Default: {})
.$_FILES - List of uploaded files (when Content-Type: multipart/form-data). (Default: {})
._http - Help functions. See list below.
- .setCookie(name, value, options = false) - Set cookie.
requestData._http.setCookie('token', 'someTokenvalue', {
domain: '.example.com',
httponly: true,
secure: true
});
- .getCookie(name) - Get cookie. Returns
false
if cookie does't exists.
const token = requestData._http.getCookie('token');
- .getCookies() - Get all cookies.
const cookies = requestData._http.getCookies();
/*
cookies = {
token: 'someTokenvalue',
cityId: 231
}
*/
.removeCookie(name) - Remove cookie.
.setCode(code) - Set status code and status message for response.
This function do following stuff:
requestData._response.statusCode = code;
requestData._response.statusMessage = http.STATUS_CODES[code];