New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

escobar

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

escobar

Node.js micro framework for REST API or any other applications based on HTTP server. 0 dependencies. Fast, simple, flexible and light.

latest
Source
npmnpm
Version
3.0.5
Version published
Maintainers
1
Created
Source

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:

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;
});

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];

Keywords

api

FAQs

Package last updated on 23 Jul 2018

Did you know?

Socket

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.

Install

Related posts