https://github.com/smikodanic/blue-router
Universal router based on Bluebird promises. Use Blue Router in NodeJS HTTP, TCP, UDP server or inside browser.
https://github.com/smikodanic/blue-router
bluebird http http-server promises router tcp tcp-server udp-server
Last synced: about 1 year ago
JSON representation
Universal router based on Bluebird promises. Use Blue Router in NodeJS HTTP, TCP, UDP server or inside browser.
- Host: GitHub
- URL: https://github.com/smikodanic/blue-router
- Owner: smikodanic
- License: mit
- Created: 2017-06-18T16:58:25.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2020-01-13T11:38:46.000Z (over 6 years ago)
- Last Synced: 2025-04-19T23:03:33.587Z (about 1 year ago)
- Topics: bluebird, http, http-server, promises, router, tcp, tcp-server, udp-server
- Language: JavaScript
- Homepage:
- Size: 25.4 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Blue Router
> Blue Router is router powered by [Bluebird](http://bluebirdjs.com/docs/getting-started.html) promises.
Routing is process that determines which function will be executed on the fly. Decision depends on URI. For example:
context.uri = '/user/register' will execute user registration function.
Blue Router can be used in NodeJS HTTP server, TCP server, UDP server, Redis PUB/SUB, browser side ...etc.
It's really universal and very fast.
## Installation
`npm install blue-router --save`
## Dependencies
The only dependency is Bluebird package. The advantage is in rich [Bluebird API](http://bluebirdjs.com/docs/api-reference.html) methods.
```javascript
const Promise = require('bluebird') //NodeJS or Browserify
//Browser (client side)
```
## Integration
```javascript
//NodeJS or browserify (client side)
const br = require('blue-router');
//Browser (client side)
```
## Methods
- **br(context).when(route)** when 'context.uri' is matched against 'route' function in then() is executed
- **br(context).when(route1).redirect(route2)** will redirect from 'route1' to 'route2'
- **br(context).notfound()** 404 not found. Always put this method after all when() methods.
- **br(context).do()** will be executed on each request
All methods when(), redirect(), notfound(), do() return bluebird promise and after that you can use any of bluebird methods (then, spread, catch, delay ...)
## Slashes
Trailing and ending slashes can be ignored so all of these URIs will be valid:
```
/cli/register/john/23/true?x=123&y=abc&z=false
/cli/register/john/23/true/?x=123&y=abc&z=false
cli/register/john/23/true?x=123&y=abc&z=false
cli/register/john/23/true/?x=123&y=abc&z=false
```
## Case Insensitive URIs
URIs are case insensitive so all of these URIs will work:
```bash
/register/john
/Register/John
/REGISTER/John
```
## Regular Expressions in route definition
[RegExp syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) is allowed in route definition.
- . any character
- \* match 0 or more times
- \+ match 1 or more times
- ? match 0 or 1 time
- | alternative
- () grouping
- [] set of chars
- {m, n} repetition modifier (at least m but at most n)
- **\\\w** word
- **\\\W** non-word
- **\\\d** digit
- **\\\D** non-digit
- ... and others
```
br(context).when('/cli/get.+/[0-9]+').then(require('./cli/match_exact.js').getname).catch(errLog);
br(context).when('/cli/shop(s)?/w{3}/:name/:year').then(require('./cli/match_param.js').shop).catch(errLog);
br(context).when('/cli/shop/\\d+/:name/:year/:color').then(require('./cli/match_param.js').shop).catch(errLog);
```
## Parameters
Variables in Blue Router are named simmilar to ExpressJS:
- **ctx.req.body** for example *context.req.body = {name: 'Peter'}*
- **ctx.req.params** for example */user/:id* and */user/23* will return *{id: 23}*
- **ctx.req.query** for example *?username=john&password=as1234* will return *{username: 'john', password: 'as1234'}*
## Chaining
Use Bluebird *then()* to serially connect functions.
This will make your code more readable and error handling easier.
**br(context).when(route).then(func11).then(func12).then(func13).catch(logErr)**
## Error 404: Not Found
Method notfound() must be placed after all when() methods.
```javascript
br(context).notfound().then(function (ctx) {console.log('Error 404: ROUTE ' + ctx.uri + ' NOT FOUND');}).catch(errLog)
```
## Debugging
To activate Blue Router debugging set **context.opts.debug: true**
## Usage
### NodeJS server (HTTP, TCP, UDP, Redis channel)
```javascript
/*
* TCP server example
*/
const br = require('blue-router');
//cmd value can be changed dynamically by TCP server
var input = {cmd: '/register/john/45', data: {company: 'Cloud LLC', employers: 257}};
var context = {
uri: input.cmd,
req: {
body: input.data
},
res: {
socket: socket
}
};
// br(context).when() is Bluebird promise
// 'ctx' is 'context' object with added req.query and req.params properties
br(context).when('/register/:name/:age')
.then(function (ctx) {
console.log('ctx.req.query: ', ctx.req.query); //undefined
console.log('ctx.req.params: ', ctx.req.params); //{name: 'John', age: 45}
console.log('ctx.req.body: ', ctx.req.body); //{company: 'Cloud LLC', employers: 257}
ctx.res.socket.write('Message to TCP client.');
})
.catch(function (err) {
console.error(err);
});
```
*Explanation:*
br(context).when() makes Bluebird promise and after that you can use all bluebird's API methods.
URI 'context.uri' can be fetched dynamically from browser's URL, HTTP/TCP/UDP server request or from command line (CLI).
## Examples
- [Command Line examples - CLI](https://github.com/smikodanic/blue-router/blob/master/examples/cli.js)
```javascript
const br = require('blue-router');
var errLog = require('./cli/errLog.js');
//input from console - cli input ($node cli.js {"cmd":"/cli/register/john/23/true","data":{}})
var input = process.argv[2];
//converting a string to object
try {
input = JSON.parse(input); //convert string to object
} catch (err) {
console.log(err.stack);
}
//context object which define router behaviour
var context = {
uri: input.cmd,
req: {
body: input.data
},
res: {
cl: console.log
},
opts: {
debug: false
}
};
///////////// R O U T E S /////////////////////
///// EXACT MATCH
//node cli.js '{"cmd": "/cli/list", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}' --> run this in Linux terminal
//node cli.js '{"cmd": "/cli/list/", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
//node cli.js '{"cmd": "cli/list", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
//node cli.js '{"cmd": "cli/list/", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
br(context).when('/cli/list').then(require('./cli/match_exact.js').list).catch(errLog);
/// redirection
//node cli.js '{"cmd": "/cli/listall", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
//node cli.js '{"cmd": "/cli/listall/", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
//node cli.js '{"cmd": "cli/listall", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
//node cli.js '{"cmd": "cli/listall/", "data": [{"id": 12}, {"id": 13}, {"id": 14}]}'
br(context).when('/cli/listall').redirect('/cli/list').then(require('./cli/match_exact.js').list).catch(errLog);
//node cli.js '{"cmd": "/cli/getname/firstname", "data": {"name": "Sasa"}}'
//node cli.js '{"cmd": "/cli/getname/firstname/", "data": {"name": "Sasa"}}'
//node cli.js '{"cmd": "cli/getname/firstname", "data": {"name": "Sasa"}}'
//node cli.js '{"cmd": "cli/getname/firstname/", "data": {"name": "Sasa"}}'
br(context).when('/cli/getname/firstname/').then(require('./cli/match_exact.js').getname).catch(errLog);
////examples with uri query string
//node cli.js '{"cmd": "/cli/login?username=peter&password=pan", "data": {}}'
//node cli.js '{"cmd": "/cli/login/?username=peter&password=pan", "data": {}}'
//node cli.js '{"cmd": "cli/login?username=peter&password=pan", "data": {}}'
//node cli.js '{"cmd": "cli/login/?username=peter&password=pan", "data": {}}'
br(context).when('/cli/login').then(require('./cli/match_exact.js').login).catch(errLog);
////examples with regular expression
//node cli.js '{"cmd": "/cli/getnames/12345", "data": {"name": "McCloud"}}'
//node cli.js '{"cmd": "/cli/getnames/12345/", "data": {"name": "McCloud"}}'
//node cli.js '{"cmd": "cli/getnames/12345", "data": {"name": "McCloud"}}'
//node cli.js '{"cmd": "cli/getnames/12345/", "data": {"name": "McCloud"}}'
//node cli.js '{"cmd": "/cli/getname/12/", "data": {"name": "McCloud"}}'
br(context).when('/cli/get.+/[0-9]+').then(require('./cli/match_exact.js').getname).catch(errLog);
///// PARAM MATCH
//node cli.js '{"cmd": "/cli/users/55", "data": [{"id": 33, "name": "Peter"}, {"id": 55, "name": "Dean"}]}'
//node cli.js '{"cmd": "/cli/users/55/", "data": [{"id": 33, "name": "Peter"}, {"id": 55, "name": "Dean"}]}'
//node cli.js '{"cmd": "cli/users/55", "data": [{"id": 33, "name": "Peter"}, {"id": 55, "name": "Dean"}]}'
//node cli.js '{"cmd": "cli/users/55/", "data": [{"id": 33, "name": "Peter"}, {"id": 55, "name": "Dean"}]}'
br(context).when('/cli/users/:id').then(require('./cli/match_param.js').get_user_by_id).catch(errLog);
//node cli.js '{"cmd": "/cli/register/john/23/true", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "/cli/register/john/23/true/", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "cli/register/john/23/true", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "cli/register/john/23/true/", "data": {"nick": "johnny"}}'
//
//examples with uri query string
//node cli.js '{"cmd": "/cli/register/john/23/true?x=123&y=abc&z=false", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "/cli/register/john/23/true/?x=123&y=abc&z=false", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "cli/register/john/23/true?x=123&y=abc&z=false", "data": {"nick": "johnny"}}'
//node cli.js '{"cmd": "cli/register/john/23/true/?x=123&y=abc&z=false", "data": {"nick": "johnny"}}'
br(context).when('/cli/register/:name/:year/:employed').then(require('./cli/match_param.js').register).catch(errLog);
///examples with regular expression
//node cli.js '{"cmd": "/cli/shops/www/CloudShop/1971", "data": {}}'
//node cli.js '{"cmd": "/cli/shop/www/CloudShop/1971", "data": {}}'
br(context).when('/cli/shop(s)?/w{3}/:name/:year').then(require('./cli/match_param.js').shop).catch(errLog);
//\\d+ replaces one or more digits (integer numbers)
//node cli.js '{"cmd": "/cli/shop/5/BetaShop/1978/red", "data": {}}'
//node cli.js '{"cmd": "/cli/shop/567/BetaShop/1978/red", "data": {}}'
br(context).when('/cli/shop/\\d+/:name/:year/:color').then(require('./cli/match_param.js').shop).catch(errLog);
///// NO MATCH (bad uri - Error 404)
//node cli.js '{"cmd": "", "data": {}}'
//node cli.js '{"cmd": "/", "data": {}}'
//node cli.js '{"cmd": "/cli", "data": {}}'
//node cli.js '{"cmd": "/cli/", "data": {}}'
//node cli.js '{"cmd": "/cli/lis", "data": {}}'
//node cli.js '{"cmd": "/cli/lista", "data": {}}'
//node cli.js '{"cmd": "/cli/list/bad", "data": {}}'
br(context).notfound().then(require('./cli/notfound.js')).catch(errLog); //put this after all when() methods
//always will be executed on each URI
br(context).do().then(require('./cli/do.js')).catch(errLog);
```
## Error Handling
```
========= errLog.js ==========
==============================
// print only messages where err.message is not empty string
module.exports = (err) => {
if (!!err && !!err.message) {
console.log('errLog: ', err.stack);
}
};
```
## Licence
*Copyright (c) 2017 Saša Mikodanić*
Licensed under [MIT](https://github.com/smikodanic/angular-form-validator/blob/master/LICENSE) .