• Stars
    star
    451
  • Rank 93,286 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 9 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Configurable, input and output validated routing for koa

joi-router

Easy, rich and fully validated koa routing.

NPM version build status Test coverage David deps npm download

Features:

Node compatibility

NodeJS >= 12 is required.

Example

const koa = require('koa');
const router = require('koa-joi-router');
const Joi = router.Joi;

const public = router();

public.get('/', async (ctx) => {
  ctx.body = 'hello joi-router!';
});

public.route({
  method: 'post',
  path: '/signup',
  validate: {
    body: {
      name: Joi.string().max(100),
      email: Joi.string().lowercase().email(),
      password: Joi.string().max(100),
      _csrf: Joi.string().token()
    },
    type: 'form',
    output: {
      200: {
        body: {
          userId: Joi.string(),
          name: Joi.string()
        }
      }
    }
  },
  handler: async (ctx) => {
    const user = await createUser(ctx.request.body);
    ctx.status = 201;
    ctx.body = user;
  }
});

const app = new koa();
app.use(public.middleware());
app.listen(3000);

Usage

koa-joi-router returns a constructor which you use to define your routes. The design is such that you construct multiple router instances, one for each section of your application which you then add as koa middleware.

const Koa = require("koa")
const router = require('koa-joi-router');

const pub = router();
const admin = router();
const auth = router();

// add some routes ..
pub.get('/some/path', async () => {});
admin.get('/admin', async () => {});
auth.post('/auth', async () => {});

const app = new Koa();
app.use(pub.middleware());
app.use(admin.middleware());
app.use(auth.middleware());
app.listen();

Module properties

.Joi

It is HIGHLY RECOMMENDED you use this bundled version of Joi to avoid bugs related to passing an object created with a different release of Joi into the router.

const koa = require('koa');
const router = require('koa-joi-router');
const Joi = router.Joi;

Router instance methods

.route()

Adds a new route to the router. route() accepts an object or array of objects describing route behavior.

const router = require('koa-joi-router');
const public = router();

public.route({
  method: 'post',
  path: '/signup',
  validate: {
    header: joiObject,
    query: joiObject,
    params: joiObject,
    body: joiObject,
    maxBody: '64kb',
    output: { '400-600': { body: joiObject } },
    type: 'form',
    failure: 400,
    continueOnError: false
  },
  pre: async (ctx, next) => {
    await checkAuth(ctx);
    return next();
  },
  handler: async (ctx) => {
    await createUser(ctx.request.body);
    ctx.status = 201;
  },
  meta: { 'this': { is: 'stored internally with the route definition' }}
});

or

const router = require('koa-joi-router');
const public = router();

const routes = [
  {
    method: 'post',
    path: '/users',
    handler: async (ctx) => {}
  },
  {
    method: 'get',
    path: '/users',
    handler: async (ctx) => {}
  }
];

public.route(routes);
.route() options
  • method: required HTTP method like "get", "post", "put", etc
  • path: required string
  • validate
    • header: object which conforms to Joi validation
    • query: object which conforms to Joi validation
    • params: object which conforms to Joi validation
    • body: object which conforms to Joi validation
    • maxBody: max incoming body size for forms or json input
    • failure: HTTP response code to use when input validation fails. default 400
    • type: if validating the request body, this is required. either form, json or multipart
    • formOptions: options for co-body form parsing when type: 'form'
    • jsonOptions: options for co-body json parsing when type: 'json'
    • multipartOptions: options for busboy parsing when type: 'multipart'
    • output: see output validation
    • continueOnError: if validation fails, this flags determines if koa-joi-router should continue processing the middleware stack or stop and respond with an error immediately. useful when you want your route to handle the error response. default false
    • validateOptions: options for Joi validate. default {}
  • handler: required async function or functions
  • pre: async function or function, will be called before parser and validators
  • meta: meta data about this route. koa-joi-router ignores this but stores it along with all other route data

.get(),post(),put(),delete() etc - HTTP methods

koa-joi-router supports the traditional router.get(), router.post() type APIs as well.

const router = require('koa-joi-router');
const admin = router();

// signature: router.method(path [, config], handler [, handler])

admin.put('/thing', handler);
admin.get('/thing', middleware, handler);
admin.post('/thing', config, handler);
admin.delete('/thing', config, middleware, handler);

.use()

Middleware run in the order they are defined by .use()(or .get(), etc.) They are invoked sequentially, requests start at the first middleware and work their way "down" the middleware stack which matches Express 4 API.

const router = require('koa-joi-router');
const users = router();

users.get('/:id', handler);
users.use('/:id', runThisAfterHandler);

.prefix()

Defines a route prefix for all defined routes. This is handy in "mounting" scenarios.

const router = require('koa-joi-router');
const users = router();

users.get('/:id', handler);
// GET /users/3 -> 404
// GET /3 -> 200

users.prefix('/users');
// GET /users/3 -> 200
// GET /3 -> 404

.param()

Defines middleware for named route parameters. Useful for auto-loading or validation.

See @koa/router

const router = require('koa-joi-router');
const users = router();

const findUser = (id) => {
  // stub
  return Promise.resolve('Cheddar');
};

users.param('user', async (id, ctx, next) => {
  const user = await findUser(id);
  if (!user) return ctx.status = 404;
  ctx.user = user;
  await next();
});

users.get('/users/:user', (ctx) => {
  ctx.body = `Hello ${ctx.user}`;
});

// GET /users/3 -> 'Hello Cheddar'

.middleware()

Generates routing middleware to be used with koa. If this middleware is never added to your koa application, your routes will not work.

const router = require('koa-joi-router');
const public = router();

public.get('/home', homepage);

const app = koa();
app.use(public.middleware()); // wired up
app.listen();

Additions to ctx.state

The route definition for the currently matched route is available via ctx.state.route. This object is not the exact same route definition object which was passed into koa-joi-router, nor is it used internally - any changes made to this object will not have an affect on your running application but is available to meet your introspection needs.

const router = require('koa-joi-router');
const public = router();
public.get('/hello', async (ctx) => {
  console.log(ctx.state.route);
});

Additions to ctx.request

When using the validate.type option, koa-joi-router adds a few new properties to ctx.request to faciliate input validation.

ctx.request.body

The ctx.request.body property will be set when either of the following validate.types are set:

  • json
  • form

json

When validate.type is set to json, the incoming data must be JSON. If it is not, validation will fail and the response status will be set to 400 or the value of validate.failure if specified. If successful, ctx.request.body will be set to the parsed request input.

admin.route({
  method: 'post',
  path: '/blog',
  validate: { type: 'json' },
  handler: async (ctx) => {
    console.log(ctx.request.body); // the incoming json as an object
  }
});

form

When validate.type is set to form, the incoming data must be form data (x-www-form-urlencoded). If it is not, validation will fail and the response status will be set to 400 or the value of validate.failure if specified. If successful, ctx.request.body will be set to the parsed request input.

admin.route({
  method: 'post',
  path: '/blog',
  validate: { type: 'form' },
  handler: async (ctx) => {
    console.log(ctx.request.body) // the incoming form as an object
  }
});

ctx.request.parts

The ctx.request.parts property will be set when either of the following validate.types are set:

  • multipart

multipart

When validate.type is set to multipart, the incoming data must be multipart data. If it is not, validation will fail and the response status will be set to 400 or the value of validate.failure if specified. If successful, ctx.request.parts will be set to an await-busboy object.

admin.route({
  method: 'post',
  path: '/blog',
  validate: { type: 'multipart' },
  handler: async (ctx) => {
    const parts = ctx.request.parts;
    let part;

    try {
      while ((part = await parts)) {
        // do something with the incoming part stream
        part.pipe(someOtherStream);
      }
    } catch (err) {
      // handle the error
    }

    console.log(parts.field.name); // form data
  }
});

Handling non-validated input

Note: if you do not specify a value for validate.type, the incoming payload will not be parsed or validated. It is up to you to parse the incoming data however you see fit.

admin.route({
  method: 'post',
  path: '/blog',
  validate: { },
  handler: async (ctx) => {
    console.log(ctx.request.body, ctx.request.parts); // undefined undefined
  }
})

Validating output

Validating the output body and/or headers your service generates on a per-status-code basis is supported. This comes in handy when contracts between your API and client are strict e.g. any change in response schema could break your downstream clients. In a very active codebase, this feature buys you stability. If the output is invalid, an HTTP status 500 will be used.

Let's look at some examples:

Validation of an individual status code

router.route({
  method: 'post',
  path: '/user',
  validate: {
    output: {
      200: { // individual status code
        body: {
          userId: Joi.string(),
          name: Joi.string()
        }
      }
    }
  },
  handler: handler
});

Validation of multiple individual status codes

router.route({
  method: 'post',
  path: '/user',
  validate: {
    output: {
      '200,201': { // multiple individual status codes
        body: {
          userId: Joi.string(),
          name: Joi.string()
        }
      }
    }
  },
  handler: handler
});

Validation of a status code range

router.route({
  method: 'post',
  path: '/user',
  validate: {
    output: {
      '200-299': { // status code range
        body: {
          userId: Joi.string(),
          name: Joi.string()
        }
      }
    }
  },
  handler: handler
});

Validation of multiple individual status codes and ranges combined

You are free to mix and match ranges and individual status codes.

router.route({
  method: 'post',
  path: '/user',
  validate: {
    output: {
      '200,201,300-600': { // mix it up
        body: {
          userId: Joi.string(),
          name: Joi.string()
        }
      }
    }
  },
  handler: handler
});

Validation of output headers

Validating your output headers is also supported via the headers property:

router.route({
  method: 'post',
  path: '/user',
  validate: {
    output: {
      '200,201': {
        body: {
          userId: Joi.string(),
          name: Joi.string()
        },
        headers: Joi.object({ // validate headers too
          authorization: Joi.string().required()
        }).options({
          allowUnknown: true
        })
      },
      '500-600': {
        body: { // this rule only runs when a status 500 - 600 is used
          error_code: Joi.number(),
          error_msg: Joi.string()
        }
      }
    }
  },
  handler: handler
});

Router instance properties

.routes

Each router exposes it's route definitions through it's routes property. This is helpful when you'd like to introspect the previous definitions and take action e.g. to generate API documentation etc.

const router = require('koa-joi-router');
const admin = router();
admin.post('/thing', { validate: { type: 'multipart' }}, handler);

console.log(admin.routes);
// [ { path: '/thing',
//     method: [ 'post' ],
//     handler: [ [Function] ],
//     validate: { type: 'multipart' } } ]

Path RegExps

Sometimes you need RegExp-like syntax support for your route definitions. Because path-to-regexp supports it, so do we!

const router = require('koa-joi-router');
const admin = router();
admin.get('/blog/:year(\\d{4})-:day(\\d{2})-:article(\\d{3})', async (ctx, next) => { 
 console.log(ctx.request.params) // { year: '2017', day: '01', article: '011' } 
});

Multiple methods support

Defining a route for multiple HTTP methods in a single shot is supported.

const router = require('koa-joi-router');
const admin = router();
admin.route({
  path: '/',
  method: ['POST', 'PUT'],
  handler: fn
});

Multiple middleware support

Often times you may need to add additional, route specific middleware to a single route.

const router = require('koa-joi-router');
const admin = router();
admin.route({
  path: '/',
  method: ['POST', 'PUT'],
  handler: [ yourMiddleware, yourHandler ]
});

Nested middleware support

You may want to bundle and nest middleware in different ways for reuse and organization purposes.

const router = require('koa-joi-router');
const admin = router();
const commonMiddleware = [ yourMiddleware, someOtherMiddleware ];
admin.route({
  path: '/',
  method: ['POST', 'PUT'],
  handler: [ commonMiddleware, yourHandler ]
});

This also works with the .get(),post(),put(),delete(), etc HTTP method helpers.

const router = require('koa-joi-router');
const admin = router();
const commonMiddleware = [ yourMiddleware, someOtherMiddleware ];
admin.get('/', commonMiddleware, yourHandler);

Handling errors

By default, koa-joi-router stops processing the middleware stack when either input validation fails. This means your route will not be reached. If this isn't what you want, for example, if you're writing a web app which needs to respond with custom html describing the errors, set the validate.continueOnError flag to true. You can find out if validation failed by checking ctx.invalid.

admin.route({
  method: 'post',
  path: '/add',
  validate: {
    type: 'form',
    body: {
      id: Joi.string().length(10)
    },
    continueOnError: true
  },
  handler: async (ctx) => {
    if (ctx.invalid) {
      console.log(ctx.invalid.header);
      console.log(ctx.invalid.query);
      console.log(ctx.invalid.params);
      console.log(ctx.invalid.body);
      console.log(ctx.invalid.type);
    }

    ctx.body = await render('add', { errors: ctx.invalid });
  }
});

Development

Running tests

  • npm test runs tests + code coverage + lint
  • npm run lint runs lint only
  • npm run lint-fix runs lint and attempts to fix syntax issues
  • npm run test-cov runs tests + test coverage
  • npm run open-cov opens test coverage results in your browser
  • npm run test-only runs tests only

LICENSE

MIT

More Repositories

1

koa

Expressive middleware for node.js using ES2017 async functions
JavaScript
34,326
star
2

examples

Example Koa apps
JavaScript
4,471
star
3

jwt

Koa middleware for validating JSON Web Tokens
JavaScript
1,333
star
4

bodyparser

Koa body parsing middleware
TypeScript
1,273
star
5

static

Static file server middleware
JavaScript
1,124
star
6

compose

Middleware composition utility
JavaScript
986
star
7

koa-body

koa body parser middleware
TypeScript
923
star
8

session

Simple session middleware for koa
JavaScript
895
star
9

router

Router middleware for Koa. Maintained by @forwardemail and @ladjs.
JavaScript
790
star
10

cors

Cross-Origin Resource Sharing(CORS) for koa
JavaScript
723
star
11

kick-off-koa

[MAINTAINERS WANTED] An intro to koa via a set of self-guided workshops
JavaScript
693
star
12

logger

Development style logging middleware
JavaScript
561
star
13

mount

Mount other Koa applications or middleware to a given pathname
JavaScript
549
star
14

ratelimit

Rate limiter middleware
JavaScript
466
star
15

route

Simple route middleware
JavaScript
442
star
16

workshop

Koa Training Workshop
JavaScript
436
star
17

compress

Compress middleware for koa
JavaScript
432
star
18

koa.io

[MAINTAINERS WANTED] Realtime web framework combine koa and socket.io.
JavaScript
427
star
19

send

Transfer static files
TypeScript
420
star
20

generic-session

koa session store with memory, redis or others.
JavaScript
414
star
21

koa-redis

Redis storage for Koa session middleware/cache with Sentinel and Cluster support
JavaScript
353
star
22

koala

[SEEKING MAINTAINER] An HTTP/2 and ES6 Module-ready Koa Suite
JavaScript
318
star
23

static-cache

[MAINTAINERS WANTED] Static cache for koa
JavaScript
293
star
24

csrf

CSRF tokens for koa
JavaScript
265
star
25

react-view

A Koa view engine which renders React components on server
JavaScript
256
star
26

convert

Convert koa generator-based middleware to promise-based middleware
JavaScript
253
star
27

ejs

a koa view render middleware, support all feature of ejs
JavaScript
245
star
28

json

pretty-printed JSON response middleware
JavaScript
197
star
29

todo

a todo example write with koa and react
JavaScript
165
star
30

koa-hbs

Handlebars templates for Koa.js
JavaScript
158
star
31

cash

HTTP response caching for Koa. Supports Redis, in-memory store, and more!
JavaScript
154
star
32

onerror

an error handler for koa, hack ctx.onerror
JavaScript
139
star
33

basic-auth

blanket basic auth middleware
JavaScript
138
star
34

multer

Middleware for handling `multipart/form-data` for koa, based on Express's multer.
JavaScript
137
star
35

userauth

koa user auth middleware
JavaScript
134
star
36

response-time

X-Response-Time middleware
JavaScript
124
star
37

trie-router

Trie-routing for Koa
JavaScript
120
star
38

koa-roles

koa version of Connect-Roles
JavaScript
117
star
39

etag

ETag support for Koa responses
JavaScript
112
star
40

bunyan-logger

Koa middleware for bunyan request logging
JavaScript
108
star
41

favicon

Koa middleware for serving a favicon
JavaScript
104
star
42

error

Error response middleware (text, json, html)
JavaScript
103
star
43

rewrite

URL rewriting middleware
JavaScript
100
star
44

json-filter

Middleware allowing the client to filter the response to only what they need, reducing the amount of traffic over the wire.
JavaScript
92
star
45

json-error

Error handler for pure-JSON apps
JavaScript
89
star
46

qs

qs for koa, and use querystring more safely.
JavaScript
85
star
47

bigpipe-example

[DEPRECATED] BigPipe using koa and component
JavaScript
83
star
48

common

[DEPRECATED] USE INDIVIDUAL MODULES
JavaScript
77
star
49

locales

koa locales, i18n solution for koa
JavaScript
67
star
50

koa-lusca

koa version of lusca. Application security for koa.
JavaScript
65
star
51

cluster

Koa clustering and error handling utility
JavaScript
64
star
52

parameter

parameter validate middleware for koa, powered by parameter
JavaScript
62
star
53

conditional-get

Conditional GET middleware for koa
JavaScript
59
star
54

trace

generic tracing for koa
JavaScript
53
star
55

koa-range

[MAINTAINERS WANTED] range request implementation for koa, see http://tools.ietf.org/html/rfc7233
JavaScript
47
star
56

sendfile

basic file-sending utility for koa
JavaScript
45
star
57

koajs.com

The koajs.com website
HTML
42
star
58

mock

Simple web page mock middleware
JavaScript
40
star
59

body-parsers

collection of koa body parsers
JavaScript
37
star
60

koa-markdown

Auto convert markdown to html for koa. Inspired by connect-markdown
JavaScript
37
star
61

koa-gzip

[Deprecated] please use koa-compress instead
JavaScript
36
star
62

file-server

file serving middleware for koa
JavaScript
36
star
63

bundle

Generic asset pipeline with caching, etags, minification, gzipping and sourcemaps.
JavaScript
36
star
64

resourcer

A simple resource directory mounter for koa.
JavaScript
34
star
65

path-match

koa route middleware
JavaScript
33
star
66

html-minifier

minify HTML responses like some crazy guy
JavaScript
30
star
67

webcam-mjpeg-stream

[DEPRECATED] Stream JPEG snapshots from your Mac
JavaScript
28
star
68

timer

time your middleware
JavaScript
26
star
69

statsd

Statsd middleware
JavaScript
24
star
70

redis-session-sets

Koa Redis sessions with field-referencing cross sets
JavaScript
20
star
71

accesslog

Middleware for common log format access logs
JavaScript
19
star
72

is-json

check if a koa body should be interpreted as JSON
JavaScript
17
star
73

charset

use iconv-lite to encode the body and set charset to content-type
JavaScript
16
star
74

koa-safe-jsonp

Safe jsonp plusins for koa.
JavaScript
16
star
75

trace-influxdb

InfluxDB tracing for koa-trace
JavaScript
15
star
76

stateless-csrf

CSRF without sessions.
JavaScript
15
star
77

atomic-session

DEPRECATED
JavaScript
15
star
78

koa-github

simple github auth middleware for koa
JavaScript
13
star
79

cross-cookies

Easily set cookies across subdomains
JavaScript
13
star
80

s3-cache

Koa middleware to cache and serve from S3
JavaScript
11
star
81

eslint-config-koa

Koa's ESLint config, based on Standard
JavaScript
11
star
82

ctx-cache-control

Augment Koa with ctx.cacheControl(maxAge)
JavaScript
9
star
83

snapshot

take snapshot when request, cache by request path.
JavaScript
9
star
84

compressor

[DEPRECATED] Compress middleware for koa that always compresses
JavaScript
9
star
85

koa-fresh

DEPRECATED
JavaScript
8
star
86

cdn

[DEPRECATED] middleware for a koa-based CDN
JavaScript
7
star
87

koa-rt

koa rt with microtime
JavaScript
6
star
88

override-method

method override utility for koa
JavaScript
6
star
89

ctx-basic-auth

Augments Koa with ctx.basicAuth
JavaScript
5
star
90

resourcer-docs

[MAINTAINERS WANTED] Simple app that generates documentation for routes mounted using koa-resourcer.
JavaScript
5
star
91

middleware-hook

low-level hooks for your middleware
JavaScript
5
star
92

path

[DEPRECATED] path-matching middleware for koa
JavaScript
4
star
93

observable-redis-session

[DEPRECATED] Observable, atomic sessions for Koa using Redis
JavaScript
2
star
94

help

koa(1) executable for instant help
2
star
95

discussions

KoaJS Discussions
1
star