Easy, rich and fully validated koa routing.
- built in input validation using joi
- built in output validation using joi
- built in body parsing using co-body and await-busboy
- built on the great @koa/router
- exposed route definitions for later analysis
- string path support
- regexp-like path support
- multiple method support
- multiple middleware support
- continue on error support
- router prefixing support
- router level middleware support
- meta data support
- HTTP 405 and 501 support
NodeJS >= 12
is required.
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);
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();
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;
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);
method
: required HTTP method like "get", "post", "put", etcpath
: required stringvalidate
header
: object which conforms to Joi validationquery
: object which conforms to Joi validationparams
: object which conforms to Joi validationbody
: object which conforms to Joi validationmaxBody
: max incoming body size for forms or json inputfailure
: HTTP response code to use when input validation fails. default400
type
: if validating the request body, this is required. eitherform
,json
ormultipart
formOptions
: options for co-body form parsing whentype: 'form'
jsonOptions
: options for co-body json parsing whentype: 'json'
multipartOptions
: options for busboy parsing whentype: 'multipart'
- any busboy constructor option. eg
{ limits: { files: 1 }}
autoFields
: Determines whether form fields should be auto-parsed (default:true
). See the await-busboy docs.
- any busboy constructor option. eg
output
: see output validationcontinueOnError
: if validation fails, this flags determines ifkoa-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. defaultfalse
validateOptions
: options for Joi validate. default{}
handler
: required async function or functionspre
: async function or function, will be called before parser and validatorsmeta
: meta data about this route.koa-joi-router
ignores this but stores it along with all other route data
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);
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);
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
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'
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();
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);
});