Introduction

Regoch Router is fast router with no dependencies for NodeJS and browser environment. Minimalistic but very powerful library. It can be used both on the client and server side. The regoch Router is implemented in websocket server and clients.


ROUTING
Routing is process that determines which function will be executed on the fly on a certain URI. For example: context.uri = '/user/register' will execute user registration function.

Features

  • intuitive API simmilar to ExpressJS middlewares
  • redirection to another route
  • not found route
  • regular expressions in route def
  • parse URI parameters (trx.params)
  • parse URI query string (trx.query)
  • convert JSON string in the object, for example: *{"uri": "/shop/prod?myJson={\"qty\": 22}"}*
  • automatically convert to number or boolean

Installation

Install the websocket client in your project via npm:


  $ npm install --save regoch-router
  

// NodeJS or browserify
const Router = require('regoch-router');
const regochRouter = new Router({debug: false});


// Browser (client side)
<script src="./Regoch.js"></script>
<script>
const router = new window.regochRouter({debug: false});
</script>
  

License

The software is published under MIT License.
The MIT License

Copyright (C) 2021  Saša Mikodanić http://www.regoch.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to
whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Router

class Router

The internal router to define what function will be executed on certain server command.

Properties

Router

PropertyDescriptionTypeDefault
routerOptsrouter options (see table below)object
trxtransitional object which passes through all route functions{uri:string, body:any, ...}
routeDefsroute definitions[{route:string, routeParsed:object, funcs:Function[] }]


routerOpts

PropertyDescriptionTypeDefault
debugshow debug messagesbooleanfalse

Transitional Variable

The transitional variable is the argument in the router middleware functions (trx) => { ... }. It's a data carrier through all route middleware functions.

When defining the input trx, the minimal and required property is uri. Sometimes body property will be also required as the payload.
Example how to define input trx:

const Router = require('regoch-router');
const regochRouter = new Router({debug: false});

regochRouter.trx = {uri: '/product/save', body: {name: 'Toy', color: 'red'}}; // define input trx
const trxOut = await regochRouter.exe(); // find matched route and execute route middlewares
  


During routing process the trx will get several useful properties which can be used in the middleware functions async trx => { console.log(trx); }. For example.

{
  uri: '/shop/register/john/23/true?x=24',
  body: {},
  uriParsed: {
    path: 'shop/register/john/23/true',
    segments: 5,
    queryString: 'x=24',
    queryObject: { x: 24 }
  },
  routeParsed: {
    full: 'shop/register/:name/:year/:employed',
    segments: 5,
    base: 'shop/register'
  },
  query: { x: 24 },
  params: { name: 'john', year: 23, employed: true }
}

  

Methods

Use this methods to control the websocket server routes.

  • constructor (routerOpts :object) :void

    Create the Router class instance.

  • set trx (obj :{uri:string, body?:any, ...}) :void

    Set the transitional variable with regochRouter.trx = {uri: '/shop/products/save', body: {id: 12, name: 'Toyota Car'}}.
    uri - required property, for example: '/product/list?limit=25'
    body - payload

  • get trx () :{uri, body, ...}

    Get the transitional variable with regochRouter.trx.

  • def (route :string, ...funcs :Function[]) :Router

    Define route, routeParsed and associate corresponding functions to the route.
    For example: regochRouter.def('/shop/login', trx => console.log(`LOGIN:: username:${trx.query.username} password:${trx.query.password}`));
    route - route string, for example: /product/list?limit=25
    funcs - functions which will be executed when route is matched, for example regochsRouter.def('/prodct/:id', f1, f2, f3, f4, f5)

  • redirect (fromRoute :string, toRoute :string, cb :Function) :Router

    Redirect from one route to another route.
    fromRoute - original route
    toRoute - destination route
    cb - callback function executed during redirection process, it's a route middleware appended to toRoute middlewares

  • notfound (...funcs :Function[]) :Router

    Execute funcs when no route found.

  • do (...funcs :Function[]) :Router

    Execute funcs always on every route.

  • async exe () :Promise<trx>

    Find the matched route and execute its middlewares.

Example


const Router = require('regoch-router');
const router = new Router({debug: false});


let trx = process.argv[2];
try {
  if (!trx) { throw new Error('Define "trx" as second parameter. For example: {"uri":"/shop/register/john/23/true","body":{}}'); }
  trx = JSON.parse(trx); // convert string to object
} catch (err) {
  console.log(err.message);
}



// route functions
const rFun1 = async (trx) => {
  console.log('fja1::', trx);
  trx.uri = 'aaa'; // will not be possible to change
  trx.body = 'bbb'; // will not be possible to change
  trx.a = 5;
  trx.a++;
};

const rFun2 = (trx) => {
  trx.b = 78;
  // throw new Error('Intentional error');
  console.log('fja2::', trx);
};


// set transitional object (which is used in )
router.trx = trx; // {uri, body, ...}



////////////////////// R O U T E S /////////////////////

/***** REGEX MATCH (NO PARAMS) (_routeRegexMatch) *****/
/* root route */
// node 005router.js '{"uri": "/"}'
router.def('/', trx => console.log('ROOT-A'), trx => console.log('ROOT-B')); // exact match

/* exact match */
// node 005router.js '{"uri": "/shop/list", "body": [{"id": 12}, {"id": 13}, {"id": 14}]}'
router.def('/shop/list', rFun1, rFun2); // exact match


/* examples with uri query string */
// node 005router.js '{"uri": "/shop/login?username=peter&password=pan"}'
router.def('/shop/login', trx => console.log(`LOGIN:: username:${trx.query.username} password:${trx.query.password}`));

// node 005router.js '{"uri": "/shop/prod?myJson={\"qty\": 22}"}'   -- parse JSON
router.def('/shop/prod', trx => console.log(`myJson:: ${JSON.stringify(trx.query.myJson)}`));


/* examples with regular expression */
// node 005router.js '{"uri": "/shop/getnames/12345"}'
router.def('/shop/get.+/[0-9]+', trx => console.log('REGEXP MATCH'));




/***** PARAM MATCH (_routeWithParamsMatch) *****/
// node 005router.js '{"uri": "/shop/users/matej/44"}'
router.def('/shop/users/:name/:age', trx => console.log(`name: ${typeof trx.params.name} ${trx.params.name} , age: ${typeof trx.params.age} ${trx.params.age}`));


/* examples with uri query string */
// node 005router.js '{"uri": "/shop/register/john/23/true?x=123&y=abc&z=false", "body": {"nick": "johnny"}}'
router.def('/shop/register/:name/:year/:employed', trx => console.log(`employed: ${trx.params.employed}`));


/* examples with regular expression */
// node 005router.js '{"uri": "/shop/shops/www/CloudShop/1971"}'
// node 005router.js '{"uri": "/shop/shop/www/CloudShop/1972"}'
router.def('/shop/shop(s)?/w{3}/:name/:year', trx => console.log(`name: ${trx.params.name} year: ${trx.params.year}`));

//// \\d+ replaces one or more digits (integer numbers)
// node 005router.js '{"uri": "/shop/shop/5/BetaShop/1978/red"}'
// node 005router.js '{"uri": "/shop/shop/567/Betashop/1979/green"}'
router.def('/SHOP/shop/\\d+/:name/:year/:color', trx => console.log(`name: ${trx.params.name} year: ${trx.params.year} color: ${trx.params.color}`));


// node 005router.js '{"uri": "/shop/shop567/{\"a\": 22}"}'
router.def('/SHOP/shop\\d+/:myJSON', trx => console.log(`myJSON: ${JSON.stringify(trx.params.myJSON)}`));


/***** REDIRECTS *****/
// node 005router.js '{"uri": "/someurl"}'
router.redirect('/someurl', '/');

// node 005router.js '{"uri": "/shop/badurl"}'
router.def('/shop/notfound', () => console.log('--SHOP ROUTE Not Found !--')).redirect('/shop/.+', '/shop/notfound'); // redirect any route to the '/notfound' route. SHOULD BE DEFINED LAST



/***** NO MATCH (bad uri - Error 404) *****/
// node 005router.js '{"uri": ""}'
// node 005router.js '{"uri": "/badurl"}'
router.notfound(trx => {console.log('Route not found.'); });




/***** DO -  always will be executed on each URI *****/
const f1 = (trx) => { console.log('always f1'); };
const f2 = (trx) => { console.log('always f2'); };
router.do(f1, f2);




/***** EXECUTE ROUTER *****/
router.exe()
  .then(trx => console.log('then(trx):: ', trx))
  .catch(err => console.log('ERRrouter:: ', err));