
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Node.js micro framework for REST API or any other applications based on HTTP server. 0 dependencies. Fast, simple, flexible and light.
Node.js micro framework for REST API or any other applications based on HTTP server.
Application example with recommended architecture is here
Node.js that supports async/await. (version 7.6 or higher has native support)
npm i --save escobar
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();
.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)
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:
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:
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;
});
requestbefore_endpointexec_routebefore_send_response.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 - 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.
requestData._http.setCookie('token', 'someTokenvalue', {
domain: '.example.com',
httponly: true,
secure: true
});
false if cookie does't exists.const token = requestData._http.getCookie('token');
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];
FAQs
Node.js micro framework for REST API or any other applications based on HTTP server. 0 dependencies. Fast, simple, flexible and light.
We found that escobar demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.