{"_id":"@dwing/koa-joi-router","_rev":"5-14dbcd15188264ad78487eb8ca8823b4","name":"@dwing/koa-joi-router","description":"Configurable, input validated routing for koa.","dist-tags":{"latest":"5.0.0"},"versions":{"5.0.0":{"name":"@dwing/koa-joi-router","version":"5.0.0","description":"Configurable, input validated routing for koa.","main":"joi-router.js","keywords":["joi","koa","router","validate","validator","validation"],"scripts":{"test":"npm run test-cov && npm run lint","lint":"NODE_ENV=test ./node_modules/eslint/bin/eslint.js test/ --quiet","lint-fix":"NODE_ENV=test ./node_modules/eslint/bin/eslint.js test/ --quiet --fix","test-cov":"NODE_ENV=test istanbul cover _mocha -- --reporter spec --bail","open-cov":"open coverage/lcov-report/index.html","test-only":"NODE_ENV=test mocha --reporter spec --bail"},"engines":{"node":">= 7.6.0"},"author":{"name":"Aaron Heckmann","email":"aaron.heckmann+github@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/koajs/joi-router.git"},"bugs":{"url":"https://github.com/koajs/joi-router/issues"},"homepage":"https://github.com/koajs/joi-router","dependencies":{"await-busboy":"1.0.1","clone":"2.1.1","co-body":"5.0.2","debug":"2.6.1","delegates":"1.0.0","flatten":"1.0.2","is-gen-fn":"0.0.1","joi":"10.2.2","koa-router":"^7.2.1","methods":"1.1.2","sliced":"1.0.1"},"devDependencies":{"co-mocha":"^1.1.0","co-supertest":"^0.0.8","eslint":"^3.17.1","eslint-config-pebble":"^4.0.0","eslint-plugin-standard":"^2.0.0","istanbul":"1.1.0-alpha.1","koa":"^2.1","mocha":"^3.0.2","supertest":"^0.15.0"},"eslintConfig":{"extends":["pebble"],"parserOptions":{"ecmaVersion":2017}},"gitHead":"645049efba4c59bd14be14d0471faea506422ca4","_id":"@dwing/koa-joi-router@5.0.0","_shasum":"7b55d79179729dc23548aae00a5a8e9e1d802514","_from":".","_npmVersion":"4.2.0","_nodeVersion":"7.10.1","_npmUser":{"name":"willin","email":"willin@willin.org"},"dist":{"shasum":"7b55d79179729dc23548aae00a5a8e9e1d802514","tarball":"https://registry.npmjs.org/@dwing/koa-joi-router/-/koa-joi-router-5.0.0.tgz","integrity":"sha512-M15gw36AAoLhTS8GSUg8S992fLttX4tYTVW0HhKgyCJrBI5qXpsuDSzSiKdSrtax+2UhEmV/Tvi87ZavkEkv7g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFA3Iaav7WqJ/2B9rG6bJKTYXqOO41D2p6dRocmTIOKHAiEAxAIb9Kl4SM6AdjHHYU8WdA1OBhKaj+fPUgqfNUczIAY="}]},"maintainers":[{"name":"willin","email":"willin@willin.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/koa-joi-router-5.0.0.tgz_1500597847220_0.1558961512055248"}}},"readme":"#joi-router\n\nEasy, rich and fully validated [koa][] routing.\n\n[![NPM version][npm-image]][npm-url]\n[![build status][travis-image]][travis-url]\n[![Test coverage][codecov-image]][codecov-url]\n[![David deps][david-image]][david-url]\n[![npm download][download-image]][download-url]\n\n[npm-image]: https://img.shields.io/npm/v/koa-joi-router.svg?style=flat-square\n[npm-url]: https://npmjs.org/package/koa-joi-router\n[travis-image]: https://img.shields.io/travis/koajs/joi-router.svg?style=flat-square\n[travis-url]: https://travis-ci.org/koajs/joi-router\n[codecov-image]: https://codecov.io/github/koajs/joi-router/coverage.svg?branch=master\n[codecov-url]: https://codecov.io/github/koajs/joi-router?branch=master\n[david-image]: https://img.shields.io/david/koajs/joi-router.svg?style=flat-square\n[david-url]: https://david-dm.org/koajs/joi-router\n[download-image]: https://img.shields.io/npm/dm/koa-joi-router.svg?style=flat-square\n[download-url]: https://npmjs.org/package/koa-joi-router\n[co]: https://github.com/tj/co\n[koa]: http://koajs.com\n[co-body]: https://github.com/visionmedia/co-body\n[await-busboy]: https://github.com/aheckmann/await-busboy\n[joi]: https://github.com/hapijs/joi\n[koa-router]: https://github.com/alexmingoia/koa-router\n[generate API documentation]: https://github.com/a-s-o/koa-docs\n[path-to-regexp]: https://github.com/pillarjs/path-to-regexp\n\n#### Features:\n\n- built in input validation using [joi][]\n- built in [output validation](#validating-output) using [joi][]\n- built in body parsing using [co-body][] and [await-busboy][]\n- built on the great [koa-router][]\n- [exposed route definitions](#routes) for later analysis\n- string path support\n- [regexp-like path support](#path-regexps)\n- [multiple method support](#multiple-methods-support)\n- [multiple middleware support](#multiple-middleware-support)\n- [continue on error support](#handling-errors)\n- [router prefixing support](#prefix)\n- [router level middleware support](#use)\n- meta data support\n- HTTP 405 and 501 support\n\n#### Node compatibility\n\nNodeJS `>= 7.6` is required.\n\n#### Example\n\n```js\nconst koa = require('koa');\nconst router = require('koa-joi-router');\nconst Joi = router.Joi;\n\nconst public = router();\n\npublic.get('/', async (ctx) => {\n  ctx.body = 'hello joi-router!';\n});\n\npublic.route({\n  method: 'post',\n  path: '/signup',\n  validate: {\n    body: {\n      name: Joi.string().max(100),\n      email: Joi.string().lowercase().email(),\n      password: Joi.string().max(100),\n      _csrf: Joi.string().token()\n    },\n    type: 'form',\n    output: {\n      200: {\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        }\n      }\n    }\n  },\n  handler: async (ctx) => {\n    const user = await createUser(ctx.request.body);\n    ctx.status = 201;\n    ctx.body = user;\n  }\n});\n\nconst app = new koa();\napp.use(public.middleware());\napp.listen(3000);\n```\n\n## Usage\n`koa-joi-router` returns a constructor which you use to define your routes.\nThe design is such that you construct multiple router instances, one for\neach section of your application which you then add as koa middleware.\n\n```js\nconst router = require('koa-joi-router');\nconst Joi = router.Joi;\n\nconst pub = router();\nconst admin = router();\nconst auth = router();\n\n// add some routes ..\npub.get('/some/path', async () => {});\nadmin.get('/admin', async () => {});\nauth.post('/auth', async () => {});\n\nconst app = koa();\nkoa.use(pub.middleware());\nkoa.use(admin.middleware());\nkoa.use(auth.middleware());\napp.listen();\n```\n\n## Module properties\n\n### .Joi\n\nIt is **HIGHLY RECOMMENDED** you use this bundled version of Joi\nto avoid bugs related to passing an object created with a different\nrelease of Joi into the router.\n\n```js\nconst koa = require('koa');\nconst router = require('koa-joi-router');\nconst Joi = router.Joi;\n```\n\n## Router instance methods\n\n### .route()\n\nAdds a new route to the router. `route()` accepts an object or array of objects\ndescribing route behavior.\n\n```js\nconst router = require('koa-joi-router');\nconst public = router();\n\npublic.route({\n  method: 'post',\n  path: '/signup',\n  validate: {\n    header: joiObject,\n    query: joiObject,\n    params: joiObject,\n    body: joiObject,\n    maxBody: '64kb',\n    output: { '400-600': { body: joiObject } },\n    type: 'form',\n    failure: 400,\n    continueOnError: false\n  },\n  handler: async (ctx) => {\n    await createUser(ctx.request.body);\n    ctx.status = 201;\n  },\n  meta: { 'this': { is: 'stored internally with the route definition' }}\n});\n```\n\nor\n\n```js\nconst router = require('koa-joi-router');\nconst public = router();\n\nconst routes = [\n  {\n    method: 'post',\n    path: '/users',\n    handler: async (ctx) => {}\n  },\n  {\n    method: 'get',\n    path: '/users',\n    handler: async (ctx) => {}\n  }\n];\n\npublic.route(routes);\n```\n\n##### .route() options\n\n- `method`: **required** HTTP method like \"get\", \"post\", \"put\", etc\n- `path`: **required** string\n- `validate`\n  - `header`: object which conforms to [Joi][] validation\n  - `query`: object which conforms to [Joi][] validation\n  - `params`: object which conforms to [Joi][] validation\n  - `body`: object which conforms to [Joi][] validation\n  - `maxBody`: max incoming body size for forms or json input\n  - `failure`: HTTP response code to use when input validation fails. default `400`\n  - `type`: if validating the request body, this is **required**. either `form`, `json` or `multipart`\n  - `output`: see [output validation](#validating-output)\n  - `continueOnError`: if validation fails, this flags determines if `koa-joi-router` should [continue processing](#handling-errors) the middleware stack or stop and respond with an error immediately. useful when you want your route to handle the error response. default `false`\n- `handler`: **required** async function or function\n- `meta`: meta data about this route. `koa-joi-router` ignores this but stores it along with all other route data\n\n### .get(),post(),put(),delete() etc - HTTP methods\n\n`koa-joi-router` supports the traditional `router.get()`, `router.post()` type APIs\nas well.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\n\n// signature: router.method(path [, config], handler [, handler])\n\nadmin.put('/thing', handler);\nadmin.get('/thing', middleware, handler);\nadmin.post('/thing', config, handler);\nadmin.delete('/thing', config, middleware, handler);\n```\n\n### .use()\n\nWhen you need to run middleware before all routes, OR, if you just need to run\nmiddleware before a specific path, this method is for you.\n\n```js\nconst router = require('koa-joi-router');\nconst users = router();\n\nusers.get('/something', async (ctx, next) => {\n  console.log('this logs before your /something handlers');\n  await next();\n  console.log('this logs after your /something handlers');\n});\n\nusers.use(async (ctx, next) => {\n  console.log('this logs before all other handlers');\n  await next();\n  console.log('this logs after all other handlers');\n});\n```\n\nIt doesn't matter if you define your routes before or after you call `.use()`,\nthe middleware passed to `.use()` will run before your routes and only when\nthe path matches.\n\nTo run middleware before a specific route, also pass the optional `path`:\n\n```js\nconst router = require('koa-joi-router');\nconst users = router();\n\nusers.get('/:id', handler);\nusers.use('/:id', runThisBeforeHandler);\n```\n\n### .prefix()\n\nDefines a route prefix for all defined routes. This is handy in \"mounting\" scenarios.\n\n```js\nconst router = require('koa-joi-router');\nconst users = router();\n\nusers.get('/:id', handler);\n// GET /users/3 -> 404\n// GET /3 -> 200\n\nusers.prefix('/user');\n// GET /users/3 -> 200\n// GET /3 -> 404\n```\n\n### .middleware()\n\nGenerates routing middleware to be used with `koa`. If this middleware is\nnever added to your `koa` application, your routes will not work.\n\n```js\nconst router = require('koa-joi-router');\nconst public = router();\n\npublic.get('/home', homepage);\n\nconst app = koa();\napp.use(public.middleware()); // wired up\napp.listen();\n```\n\n## Additions to ctx.state\n\nThe route definition for the currently matched route is available\nvia `ctx.state.route`. This object is not the exact same route\ndefinition object which was passed into koa-joi-router, nor is it\nused internally - any changes made to this object will\nnot have an affect on your running application but is available\nto meet your introspection needs.\n\n```js\nconst router = require('koa-joi-router');\nconst public = router();\npublic.get('/hello', async (ctx) => {\n  console.log(ctx.state.route);\n});\n```\n\n## Additions to ctx.request\n\nWhen using the `validate.type` option, `koa-joi-router` adds a few new properties\nto `ctx.request` to faciliate input validation.\n\n### ctx.request.body\n\nThe `ctx.request.body` property will be set when either of the following\n`validate.type`s are set:\n\n- json\n- form\n\n#### json\n\nWhen `validate.type` is set to `json`, the incoming data must be JSON. If it is not,\nvalidation will fail and the response status will be set to 400 or the value of\n`validate.failure` if specified. If successful, `ctx.request.body` will be set to the\nparsed request input.\n\n```js\nadmin.route({\n  method: 'post',\n  path: '/blog',\n  validate: { type: 'json' },\n  handler: async (ctx) => {\n    console.log(ctx.request.body); // the incoming json as an object\n  }\n});\n```\n\n#### form\n\nWhen `validate.type` is set to `form`, the incoming data must be form data\n(x-www-form-urlencoded). If it is not, validation will fail and the response\nstatus will be set to 400 or the value of `validate.failure` if specified.\nIf successful, `ctx.request.body` will be set to the parsed request input.\n\n```js\nadmin.route({\n  method: 'post',\n  path: '/blog',\n  validate: { type: 'form' },\n  handler: async (ctx) => {\n    console.log(ctx.request.body) // the incoming form as an object\n  }\n});\n```\n\n### ctx.request.parts\n\nThe `ctx.request.parts` property will be set when either of the following\n`validate.type`s are set:\n\n- multipart\n\n#### multipart\n\nWhen `validate.type` is set to `multipart`, the incoming data must be multipart data.\nIf it is not, validation will fail and the response\nstatus will be set to 400 or the value of `validate.failure` if specified.\nIf successful, `ctx.request.parts` will be set to an\n[await-busboy][] object.\n\n```js\nadmin.route({\n  method: 'post',\n  path: '/blog',\n  validate: { type: 'multipart' },\n  handler: async (ctx) => {\n    const parts = ctx.request.parts;\n    let part;\n\n    try {\n      while ((part = await parts)) {\n        // do something with the incoming part stream\n        part.pipe(someOtherStream);\n      }\n    } catch (err) {\n      // handle the error\n    }\n\n    console.log(parts.field.name); // form data\n  }\n});\n```\n\n## Handling non-validated input\n\n_Note:_ if you do not specify a value for `validate.type`, the\nincoming payload will not be parsed or validated. It is up to you to\nparse the incoming data however you see fit.\n\n```js\nadmin.route({\n  method: 'post',\n  path: '/blog',\n  validate: { },\n  handler: async (ctx) => {\n    console.log(ctx.request.body, ctx.request.parts); // undefined undefined\n  }\n})\n```\n\n## Validating output\n\nValidating the output body and/or headers your service generates on a\nper-status-code basis is supported. This comes in handy when contracts\nbetween your API and client are strict e.g. any change in response\nschema could break your downstream clients. In a very active codebase, this\nfeature buys you stability. If the output is invalid, an HTTP status 500\nwill be used.\n\nLet's look at some examples:\n\n### Validation of an individual status code\n\n```js\nrouter.route({\n  method: 'post',\n  path: '/user',\n  validate: {\n    output: {\n      200: { // individual status code\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        }\n      }\n    }\n  },\n  handler: handler\n});\n```\n\n### Validation of multiple individual status codes\n\n```js\nrouter.route({\n  method: 'post',\n  path: '/user',\n  validate: {\n    output: {\n      '200,201': { // multiple individual status codes\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        }\n      }\n    }\n  },\n  handler: handler\n});\n```\n\n### Validation of a status code range\n\n```js\nrouter.route({\n  method: 'post',\n  path: '/user',\n  validate: {\n    output: {\n      '200-299': { // status code range\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        }\n      }\n    }\n  },\n  handler: handler\n});\n```\n\n### Validation of multiple individual status codes and ranges combined\n\nYou are free to mix and match ranges and individual status codes.\n\n```js\nrouter.route({\n  method: 'post',\n  path: '/user',\n  validate: {\n    output: {\n      '200,201,300-600': { // mix it up\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        }\n      }\n    }\n  },\n  handler: handler\n});\n```\n\n### Validation of output headers\n\nValidating your output headers is also supported via the `headers` property:\n\n```js\nrouter.route({\n  method: 'post',\n  path: '/user',\n  validate: {\n    output: {\n      '200,201': {\n        body: {\n          userId: Joi.string(),\n          name: Joi.string()\n        },\n        headers: Joi.object({ // validate headers too\n          authorization: Joi.string().required()\n        }).options({\n          allowUnknown: true\n        })\n      },\n      '500-600': {\n        body: { // this rule only runs when a status 500 - 600 is used\n          error_code: Joi.number(),\n          error_msg: Joi.string()\n        }\n      }\n    }\n  },\n  handler: handler\n});\n```\n\n## Router instance properties\n\n### .routes\n\nEach router exposes it's route definitions through it's `routes` property.\nThis is helpful when you'd like to introspect the previous definitions and\ntake action e.g. to [generate API documentation][] etc.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nadmin.post('/thing', { validate: { type: 'multipart' }}, handler);\n\nconsole.log(admin.routes);\n// [ { path: '/thing',\n//     method: [ 'post' ],\n//     handler: [ [Function] ],\n//     validate: { type: 'multipart' } } ]\n```\n\n## Path RegExps\n\nSometimes you need `RegExp`-like syntax support for your route definitions.\nBecause [path-to-regexp][]\nsupports it, so do we!\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nadmin.get('/blog/:year(\\\\d{4})-:day(\\\\d{2})-:article(\\\\d{3})', async (ctx, next) => { \n console.log(ctx.request.params) // { year: '2017', day: '01', article: '011' } \n});\n```\n\n## Multiple methods support\n\nDefining a route for multiple HTTP methods in a single shot is supported.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nadmin.route({\n  path: '/',\n  method: ['POST', 'PUT'],\n  handler: fn\n});\n```\n\n## Multiple middleware support\n\nOften times you may need to add additional, route specific middleware to a\nsingle route.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nadmin.route({\n  path: '/',\n  method: ['POST', 'PUT'],\n  handler: [ yourMiddleware, yourHandler ]\n});\n```\n\n## Nested middleware support\n\nYou may want to bundle and nest middleware in different ways for reuse and\norganization purposes.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nconst commonMiddleware = [ yourMiddleware, someOtherMiddleware ];\nadmin.route({\n  path: '/',\n  method: ['POST', 'PUT'],\n  handler: [ commonMiddleware, yourHandler ]\n});\n```\n\nThis also works with the .get(),post(),put(),delete(), etc HTTP method helpers.\n\n```js\nconst router = require('koa-joi-router');\nconst admin = router();\nconst commonMiddleware = [ yourMiddleware, someOtherMiddleware ];\nadmin.get('/', commonMiddleware, yourHandler);\n```\n\n## Handling errors\n\nBy default, `koa-joi-router` stops processing the middleware stack when either\ninput validation fails. This means your route will not be reached. If\nthis isn't what you want, for example, if you're writing a web app which needs\nto respond with custom html describing the errors, set the `validate.continueOnError`\nflag to true. You can find out if validation failed by checking `ctx.invalid`.\n\n```js\nadmin.route({\n  method: 'post',\n  path: '/add',\n  validate: {\n    type: 'form',\n    body: {\n      id: Joi.string().length(10)\n    },\n    continueOnError: true\n  },\n  handler: async (ctx) => {\n    if (ctx.invalid) {\n      console.log(ctx.invalid.header);\n      console.log(ctx.invalid.query);\n      console.log(ctx.invalid.params);\n      console.log(ctx.invalid.body);\n      console.log(ctx.invalid.type);\n    }\n\n    ctx.body = await render('add', { errors: ctx.invalid });\n  }\n});\n```\n\n## Development\n\n### Running tests\n\n- `npm test` runs tests + code coverage + lint\n- `npm run lint` runs lint only\n- `npm run lint-fix` runs lint and attempts to fix syntax issues\n- `npm run test-cov` runs tests + test coverage\n- `npm run open-cov` opens test coverage results in your browser\n- `npm run test-only` runs tests only\n\n## LICENSE\n\n[MIT](https://github.com/koajs/joi-router/blob/master/LICENSE)\n\n","maintainers":[],"time":{"modified":"2022-06-12T17:23:15.695Z","created":"2017-07-21T00:44:07.322Z","5.0.0":"2017-07-21T00:44:07.322Z"},"homepage":"https://github.com/koajs/joi-router","keywords":["joi","koa","router","validate","validator","validation"],"repository":{"type":"git","url":"git+https://github.com/koajs/joi-router.git"},"author":{"name":"Aaron Heckmann","email":"aaron.heckmann+github@gmail.com"},"bugs":{"url":"https://github.com/koajs/joi-router/issues"},"license":"MIT","readmeFilename":"README.md"}