{"id":23537489,"url":"https://github.com/smikodanic/blue-router","last_synced_at":"2025-05-15T00:20:53.982Z","repository":{"id":121138341,"uuid":"94700864","full_name":"smikodanic/blue-router","owner":"smikodanic","description":"Universal router based on Bluebird promises. Use Blue Router in NodeJS HTTP, TCP, UDP server or inside browser.","archived":false,"fork":false,"pushed_at":"2020-01-13T11:38:46.000Z","size":26,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T23:03:33.587Z","etag":null,"topics":["bluebird","http","http-server","promises","router","tcp","tcp-server","udp-server"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smikodanic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-18T16:58:25.000Z","updated_at":"2020-01-13T11:38:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"f5417cd2-d81c-46ef-995a-47d41edd8012","html_url":"https://github.com/smikodanic/blue-router","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smikodanic%2Fblue-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smikodanic%2Fblue-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smikodanic%2Fblue-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smikodanic%2Fblue-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smikodanic","download_url":"https://codeload.github.com/smikodanic/blue-router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254249264,"owners_count":22039037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bluebird","http","http-server","promises","router","tcp","tcp-server","udp-server"],"created_at":"2024-12-26T03:15:46.392Z","updated_at":"2025-05-15T00:20:53.976Z","avatar_url":"https://github.com/smikodanic.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Blue Router\n\u003e Blue Router is router powered by [Bluebird](http://bluebirdjs.com/docs/getting-started.html) promises.\nRouting is process that determines which function will be executed on the fly. Decision depends on URI. For example:\ncontext.uri = '/user/register' will execute user registration function.\n\nBlue Router can be used in NodeJS HTTP server, TCP server, UDP server, Redis PUB/SUB, browser side ...etc.\n\nIt's really universal and very fast.\n\n\n## Installation\n`npm install blue-router --save`\n\n\n## Dependencies\nThe only dependency is Bluebird package. The advantage is in rich [Bluebird API](http://bluebirdjs.com/docs/api-reference.html) methods.\n```javascript\nconst Promise = require('bluebird') //NodeJS or Browserify\n\u003cscript src=\"bluebird.min.js\"\u003e\u003c/script\u003e //Browser (client side)\n```\n\n\n## Integration\n```javascript\n//NodeJS or browserify (client side)\nconst br = require('blue-router');\n\n//Browser (client side)\n\u003cscript src=\"bluebird.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/blue-router/index.js\"\u003e\u003c/script\u003e\n```\n\n\n## Methods\n- **br(context).when(route)**    when 'context.uri' is matched against 'route' function in then() is executed\n- **br(context).when(route1).redirect(route2)**    will redirect from 'route1' to 'route2'\n- **br(context).notfound()**    404 not found. Always put this method after all when() methods.\n- **br(context).do()**    will be executed on each request\n\nAll methods when(), redirect(), notfound(), do() return bluebird promise and after that you can use any of bluebird methods (then, spread, catch, delay ...)\n\n\n\n## Slashes\nTrailing and ending slashes can be ignored so all of these URIs will be valid:\n```\n/cli/register/john/23/true?x=123\u0026y=abc\u0026z=false\n/cli/register/john/23/true/?x=123\u0026y=abc\u0026z=false\ncli/register/john/23/true?x=123\u0026y=abc\u0026z=false\ncli/register/john/23/true/?x=123\u0026y=abc\u0026z=false\n```\n\n\n## Case Insensitive URIs\nURIs are case insensitive so all of these URIs will work:\n```bash\n/register/john\n/Register/John\n/REGISTER/John\n```\n\n\n## Regular Expressions in route definition\n[RegExp syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) is allowed in route definition.\n- . any character\n- \\* match 0 or more times\n- \\+ match 1 or more times\n- ? match 0 or 1 time\n- | alternative\n- () grouping\n- [] set of chars\n- {m, n} repetition modifier (at least m but at most n)\n- **\\\\\\w**  word\n- **\\\\\\W**  non-word\n- **\\\\\\d**  digit\n- **\\\\\\D**  non-digit\n- ... and others\n```\nbr(context).when('/cli/get.+/[0-9]+').then(require('./cli/match_exact.js').getname).catch(errLog);\nbr(context).when('/cli/shop(s)?/w{3}/:name/:year').then(require('./cli/match_param.js').shop).catch(errLog);\nbr(context).when('/cli/shop/\\\\d+/:name/:year/:color').then(require('./cli/match_param.js').shop).catch(errLog);\n```\n\n\n## Parameters\nVariables in Blue Router are named simmilar to ExpressJS:\n- **ctx.req.body** for example *context.req.body = {name: 'Peter'}*\n- **ctx.req.params** for example */user/:id*  and */user/23* will return *{id: 23}*\n- **ctx.req.query** for example *?username=john\u0026password=as1234* will return *{username: 'john', password: 'as1234'}*\n\n\n\n## Chaining\nUse Bluebird *then()* to serially connect functions.\nThis will make your code more readable and error handling easier.\n**br(context).when(route).then(func11).then(func12).then(func13).catch(logErr)**\n\n\n\n## Error 404: Not Found\nMethod notfound() must be placed after all when() methods.\n```javascript\nbr(context).notfound().then(function (ctx) {console.log('Error 404: ROUTE ' + ctx.uri + ' NOT FOUND');}).catch(errLog)\n```\n\n\n## Debugging\nTo activate Blue Router debugging set **context.opts.debug: true**\n\n\n\n## Usage\n\n### NodeJS server (HTTP, TCP, UDP, Redis channel)\n\n```javascript\n/*\n * TCP server example\n */\nconst br = require('blue-router');\n\n//cmd value can be changed dynamically by TCP server\nvar input = {cmd: '/register/john/45', data: {company: 'Cloud LLC', employers: 257}};\n\nvar context = {\n    uri: input.cmd,\n    req: {\n        body: input.data\n    },\n    res: {\n        socket: socket\n    }\n};\n\n// br(context).when() is Bluebird promise\n// 'ctx' is 'context' object with added req.query and req.params properties\nbr(context).when('/register/:name/:age')\n    .then(function (ctx) {\n        console.log('ctx.req.query: ', ctx.req.query); //undefined\n        console.log('ctx.req.params: ', ctx.req.params); //{name: 'John', age: 45}\n        console.log('ctx.req.body: ', ctx.req.body); //{company: 'Cloud LLC', employers: 257}\n\n        ctx.res.socket.write('Message to TCP client.');\n    })\n    .catch(function (err) {\n        console.error(err);\n    });\n\n```\n*Explanation:*\nbr(context).when() makes Bluebird promise and after that you can use all bluebird's API methods.\nURI 'context.uri' can be fetched dynamically from browser's URL, HTTP/TCP/UDP server request or from command line (CLI).\n\n\n\n## Examples\n\n- [Command Line examples - CLI](https://github.com/smikodanic/blue-router/blob/master/examples/cli.js)\n\n```javascript\nconst br = require('blue-router');\nvar errLog = require('./cli/errLog.js');\n\n//input from console - cli input ($node cli.js {\"cmd\":\"/cli/register/john/23/true\",\"data\":{}})\nvar input = process.argv[2];\n\n//converting a string to object\ntry {\n    input = JSON.parse(input); //convert string to object\n} catch (err) {\n    console.log(err.stack);\n}\n\n//context object which define router behaviour\nvar context = {\n    uri: input.cmd,\n    req: {\n        body: input.data\n    },\n    res: {\n        cl: console.log\n    },\n    opts: {\n        debug: false\n    }\n};\n\n\n\n\n///////////// R O U T E S /////////////////////\n\n\n///// EXACT MATCH\n\n//node cli.js '{\"cmd\": \"/cli/list\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'  --\u003e run this in Linux terminal\n//node cli.js '{\"cmd\": \"/cli/list/\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\n//node cli.js '{\"cmd\": \"cli/list\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\n//node cli.js '{\"cmd\": \"cli/list/\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\nbr(context).when('/cli/list').then(require('./cli/match_exact.js').list).catch(errLog);\n\n/// redirection\n//node cli.js '{\"cmd\": \"/cli/listall\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\n//node cli.js '{\"cmd\": \"/cli/listall/\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\n//node cli.js '{\"cmd\": \"cli/listall\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\n//node cli.js '{\"cmd\": \"cli/listall/\", \"data\": [{\"id\": 12}, {\"id\": 13}, {\"id\": 14}]}'\nbr(context).when('/cli/listall').redirect('/cli/list').then(require('./cli/match_exact.js').list).catch(errLog);\n\n//node cli.js '{\"cmd\": \"/cli/getname/firstname\", \"data\": {\"name\": \"Sasa\"}}'\n//node cli.js '{\"cmd\": \"/cli/getname/firstname/\", \"data\": {\"name\": \"Sasa\"}}'\n//node cli.js '{\"cmd\": \"cli/getname/firstname\", \"data\": {\"name\": \"Sasa\"}}'\n//node cli.js '{\"cmd\": \"cli/getname/firstname/\", \"data\": {\"name\": \"Sasa\"}}'\nbr(context).when('/cli/getname/firstname/').then(require('./cli/match_exact.js').getname).catch(errLog);\n\n\n////examples with uri query string\n//node cli.js '{\"cmd\": \"/cli/login?username=peter\u0026password=pan\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/login/?username=peter\u0026password=pan\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"cli/login?username=peter\u0026password=pan\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"cli/login/?username=peter\u0026password=pan\", \"data\": {}}'\nbr(context).when('/cli/login').then(require('./cli/match_exact.js').login).catch(errLog);\n\n\n////examples with regular expression\n//node cli.js '{\"cmd\": \"/cli/getnames/12345\", \"data\": {\"name\": \"McCloud\"}}'\n//node cli.js '{\"cmd\": \"/cli/getnames/12345/\", \"data\": {\"name\": \"McCloud\"}}'\n//node cli.js '{\"cmd\": \"cli/getnames/12345\", \"data\": {\"name\": \"McCloud\"}}'\n//node cli.js '{\"cmd\": \"cli/getnames/12345/\", \"data\": {\"name\": \"McCloud\"}}'\n//node cli.js '{\"cmd\": \"/cli/getname/12/\", \"data\": {\"name\": \"McCloud\"}}'\nbr(context).when('/cli/get.+/[0-9]+').then(require('./cli/match_exact.js').getname).catch(errLog);\n\n\n\n\n\n\n///// PARAM MATCH\n\n//node cli.js '{\"cmd\": \"/cli/users/55\", \"data\": [{\"id\": 33, \"name\": \"Peter\"}, {\"id\": 55, \"name\": \"Dean\"}]}'\n//node cli.js '{\"cmd\": \"/cli/users/55/\", \"data\": [{\"id\": 33, \"name\": \"Peter\"}, {\"id\": 55, \"name\": \"Dean\"}]}'\n//node cli.js '{\"cmd\": \"cli/users/55\", \"data\": [{\"id\": 33, \"name\": \"Peter\"}, {\"id\": 55, \"name\": \"Dean\"}]}'\n//node cli.js '{\"cmd\": \"cli/users/55/\", \"data\": [{\"id\": 33, \"name\": \"Peter\"}, {\"id\": 55, \"name\": \"Dean\"}]}'\nbr(context).when('/cli/users/:id').then(require('./cli/match_param.js').get_user_by_id).catch(errLog);\n\n//node cli.js '{\"cmd\": \"/cli/register/john/23/true\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"/cli/register/john/23/true/\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"cli/register/john/23/true\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"cli/register/john/23/true/\", \"data\": {\"nick\": \"johnny\"}}'\n//\n//examples with uri query string\n//node cli.js '{\"cmd\": \"/cli/register/john/23/true?x=123\u0026y=abc\u0026z=false\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"/cli/register/john/23/true/?x=123\u0026y=abc\u0026z=false\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"cli/register/john/23/true?x=123\u0026y=abc\u0026z=false\", \"data\": {\"nick\": \"johnny\"}}'\n//node cli.js '{\"cmd\": \"cli/register/john/23/true/?x=123\u0026y=abc\u0026z=false\", \"data\": {\"nick\": \"johnny\"}}'\nbr(context).when('/cli/register/:name/:year/:employed').then(require('./cli/match_param.js').register).catch(errLog);\n\n\n///examples with regular expression\n\n//node cli.js '{\"cmd\": \"/cli/shops/www/CloudShop/1971\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/shop/www/CloudShop/1971\", \"data\": {}}'\nbr(context).when('/cli/shop(s)?/w{3}/:name/:year').then(require('./cli/match_param.js').shop).catch(errLog);\n\n//\\\\d+ replaces one or more digits (integer numbers)\n//node cli.js '{\"cmd\": \"/cli/shop/5/BetaShop/1978/red\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/shop/567/BetaShop/1978/red\", \"data\": {}}'\nbr(context).when('/cli/shop/\\\\d+/:name/:year/:color').then(require('./cli/match_param.js').shop).catch(errLog);\n\n\n\n\n\n///// NO MATCH (bad uri - Error 404)\n\n//node cli.js '{\"cmd\": \"\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/lis\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/lista\", \"data\": {}}'\n//node cli.js '{\"cmd\": \"/cli/list/bad\", \"data\": {}}'\nbr(context).notfound().then(require('./cli/notfound.js')).catch(errLog); //put this after all when() methods\n\n\n\n//always will be executed on each URI\nbr(context).do().then(require('./cli/do.js')).catch(errLog);\n```\n\n\n## Error Handling\n```\n========= errLog.js ==========\n==============================\n// print only messages where err.message is not empty string\nmodule.exports = (err) =\u003e {\n  if (!!err \u0026\u0026 !!err.message) {\n    console.log('errLog: ', err.stack);\n  }\n};\n```\n\n\n\n\n\n## Licence\n*Copyright (c) 2017 Saša Mikodanić*\nLicensed under [MIT](https://github.com/smikodanic/angular-form-validator/blob/master/LICENSE) .\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmikodanic%2Fblue-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmikodanic%2Fblue-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmikodanic%2Fblue-router/lists"}