npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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

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:

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