express-requestprocessor
TypeScript icon, indicating that this package has built-in type declarations

0.0.0-beta23 • Public • Published

RequestProcessor for express

version downloads license coverage report pipeline status

RequestProcessor is used to build all routes from a folder for an api using express.

If you have any trouble with the RequestProcessor, just try the demo or open an issue!

Controlling the Request Processor

Before starting the RequestProcessor, you need to configure it. After that you can start/stop/or restart it using just a single command.

const RequestProcessor = require('express-requestprocessor');
const Config = {
        log: {
            level: RequestProcessor.Logger.levels.DEBUG,
            output: (out, message) => console.log(message),
        },
        serviceFolder: '/services',
        port: 5000,
        authorize: authorizeUsingBasicAuth,
        requestSize: '1mb',
        notFoundPage: (req, res) => {
            return 'Sorry this page was not found!';
          },
        docs: {
            route: '/docs',
            title: 'Request Processor Demo Services',
            description: 'For demonstration purpose!',
            version: '1.0.0',
            host: 'localhost:8080',
            basePath: '',
            additionalDocsPath: ["src/types/**/*.ts"],
            produces: ['application/json'],
            securityDefinitions: {
              basic: {
                type: 'basic',
              },
            },
          },
    };
RequestProcessor
    .configure(Config)
    .start();

Configuration Items

  • log.level optional ENUM: Level of Logging, Possible Values: RequestProcessor.Logger.levels.[TRACE, DEBUG, INFO, WARNING, ERROR]. If not set every message will be logged (similar to level TRACE).
  • log.output required function: Output function for log messages (you can do whatever you want with the long entries). This output includes an object with raw details (level, message, source, details) and a parsed message containing all elements of the object.
  • serviceFolder required String: Folder path, starting from your project root, where services are located (subfolders are also allowed).
  • apiBasePath: optional String: If the api should run in a subfolder (e.g. /api), you need to specify it here.
  • port required Number: Port where the api will be available.
  • authorize optional Function: Authorizer function that will be called for every request to handle the authorization.
  • requestSize optional String: Maximum size of a request (if it's larger status 413 will be returned). Sample values: 1b, 10kb 20mb, 30gb, 40tb, 50pb, for more information visit bytes.js readme
  • notFoundPage optional Handler Object: Handles all requests, that does not fit to any provided service. You just need to provide a Handler Object in the config.
  • docs optional boolean/object: Enables Swagger docs. Visit Doc definition chapter for more information.
  • docs.route optional string: Path to swagger docs, by default {apiBaseUrl}/docs.
  • docs.title optional string: Title of your api.
  • docs.description optional string: Description of your api.
  • docs.version optional string: Api Version.
  • docs.host optional string: Host name of your api.
  • docs.basePath optional string: Path to your api (e.g. if it is in a subdirectory), by default apiBasePath is used, but you can override it with this value.
  • docs.additionalDocsPath optional string[]: Additional path for your swagger docs. If you want to add docs from another folder than the serviceFolder than you have to add it here.
  • docs.schemes optional string[]: You can specify schemes like http or https here, default ['http', 'https'].
  • docs.produces optional string[]: Produces header, if you want to provide anything else or more than JSON, e.g. application/json, application/xml.
  • docs.securityDefinitions optional string: Adds support for Authentication. For basic auth you need to insert {basic: {type: basic}}, if you want to support other authentication techniques refer to the documentation

Defining Services

For defining services you need to call RequestProcessor.process(...) inside your service, that is located in your services folder.

You can call the process function with following parameters:

RequestProcessor.process({
        path: "/test",                  // this is the endpoint path
        get: handleGet,                 // handler functions can be configured the simple way without additional information
        role: "testrole",               //todo
        post: {
                handle: handlePost,     // or inside a special object
                validate: validatePost,  // e.g. if you want to use the validator that is
                role: "testpostrole",    // todo
                link: {                 // link header configuration (can be an array or an object)
                    name: 'Dispatcher', // name of the service that should be linked
                    url: '/',           // service path
                  }
        },
        put: handlePut,
        delete: handleDelete,
        validate: validate,              // this validator gets only be called when using the GET method
        link: [                         // link header configuration (can be a array or an object)
            {
              name: 'Test 1',           // name of the service that should be linked
              url: '/test/1',           // service path
            },
            {
              name: 'Test 2',
              url: '/test/2',
            }
        ]
});

It is possible to set configration inside and outside the request method configuration (GET, POST, PUT, DELETE). If you set it outside, it will be used if there is nothing defined inside.

You can provide a Handler and a Validator function for each method. The validator function should validate inputs, a hander should process the request and return a result.

Validator

You can put validator functions (that should be named validate or validateMethod e.g. validateGet) inside the config object or inside get/post/put/delete. If its inside the function will only be called if this method is used. If it is outside it will be called for all methods that do not have an own validate function inside.

A validator should return true or nothing if the validation was successful otherwise it should return false or throw an exception.

The handler method gets only a request object.

Request object (mostly called req):

let req = {
    "method": "GET",
    "query": {
        "name1": "value1",
        "name2": "value2"
    },
    "path": "/test/where/am/i",
    "headers": {
        "name1": "value1",
        "name2": "value2"
    },
    "body": {
        "about": "this object can also be an array, a string, a number or anything else"
    },
    "auth": "Return value of an Authorizer if provided (can be used for user data)",
    "role": "role provided in RequestProcessor.process('this will be provided')",
    "endpoint": "/service/:id"
}

Handler

You can add a handler to one of get/post/put/delete variables or an object that contains a variable called handler with a handler function inside. That is useful if you want to use the validator function.

The handler method gets a request and a response object.

Request object (mostly called req):

let req = {
    "method": "GET",
    "query": {
        "name1": "value1",
        "name2": "value2"
    },
    "path": "/test/where/am/i",
    "headers": {
        "name1": "value1",
        "name2": "value2"
    },
    "body": {
        "about": "this object can also be an array, a string, a number or anything else"
    },
    "auth": "Return value of an Authorizer if provided (can be used for user data)",
    "role": "role provided in RequestProcessor.process('this will be provided"
}

Response object (mostly called res):

let res = {
    "status": 200,          // status code
    "body": {               // any bode that should be returned
        "about": "this object can also be an array, a string, a number or anything else"
    },
    "header": {             // provide any header you want
        "name1": "value1",
        "name2": "value2"
    },
    cookie: {               // provide any cookies you want
        "name": {                                       // cookie name
            value: "anyCookieValue",                // value of this cookie
            maxAge: 36000000,                       // max age in ms (36000000ms = 1h)
            domain: "minis-lioba.de"                // domain for this cookie (only subdomains on the same domain are allowed)
        }
    },
    "file": "test.png"      // You can any file that will be returned, but if you provide a value the file will be sent to the user instead of a body.
}

Authorizer

Providing an authorization function is only optional.

This function will be called every time an endpoint is called. It should be used to verify using headers, roles or something else wheater a user is allowed to access this endpoint or not. If so, an object could be returned that will be forwarded to the Validator and Handler. If not, an exception should can be thrown and the user gets an error message (Validator and Handler won't be called).

A authorize function can look like that:

const allowedUsers = [
  {
    name: 'user',
    password: 'pass',
  },
  {
    name: 'test',
    password: 'test',
  },
];

const authorizeBasicAuth = function(req) {
  const trowable = {status: 401, message: 'Authorization failed'};
  if (!req.headers.authorization) throw trowable;
  let authHeader = req.headers.authorization;
  if (!authHeader.startsWith('Basic ')) throw trowable;
  authHeader = authHeader.replace('Basic ', '');
  const login = Buffer.from(authHeader, 'base64').toString().split(':');

  for (let i=0; i<allowedUsers.length; ++i) {
    if (allowedUsers[i].name === login[0]
      && allowedUsers[i].password === login[1]) return {user: login[0]};
  }
  throw trowable;
};

Automatic Status Codes

The RequestProcessor will automatically return following status codes:

  • 200: If everything was ok and nothing special happened
  • 201: If everything was ok for POST method
  • 204: If the response body is empty, but the request was processed successfully
  • 400: If the validator function throws an error or returns false
  • 500: If the handle function throws an error

You can always override this automatic status codes by changing the res.status variable in the handle function (res.status=200) or by throwing an error with a status value inside the error object (throw {status: 500, message: "Just an error"})

Typescript Support

This software supports Typescript. You can use it in the common way.

Useful types:

Docs

First you need to enable docs in the config.

After that you can create a doc definition just inside the service. Therefor you have to create a jsdoc with following keywords:

  • just inside the jsdoc: Description of the service
  • @group Create a group with multiple services/http method
  • @route: Define http method and path like @route GET /sum, you can also define path params @route GET /sum/{a}/{b}.
  • @param: Defines a request param, Syntax: @param {type} name.paramType.required Description: if you provide .required than the user is force to fill this param.
    • Query Params: e.g. @param {Integer} a.query Param a, @param {Object} b.query.required Param b
    • Path Params: e.g. @param {Integer} a.path.required Variable a
  • @return: Adds return status code an description e.g. @return {type} 200 - Description

For more information try the demo api or have a look at this documentation

Demo API

To see how the RequestProcessor is working, it is better to look at the demo files:

  • git clone git@gitlab.com:FelixFranz/express-requestprocessor.git
  • npm install
  • npm start
  • Open the Address displayed in the console
  • Look at test/services for the service implementation

Package Sidebar

Install

npm i express-requestprocessor

Weekly Downloads

1

Version

0.0.0-beta23

License

MIT

Unpacked Size

32.8 kB

Total Files

9

Last publish

Collaborators

  • felix-franz