koajs / joi-router Goto Github PK
View Code? Open in Web Editor NEWConfigurable, input and output validated routing for koa
License: MIT License
Configurable, input and output validated routing for koa
License: MIT License
Hello everyone,
I saw the docs and I have managed to successfully receive every type of requests except for multipart..
I copied the given code:
``admin.route({
method: 'post'
, path: '/blog'
, validate: { type: 'multipart' }
, handler: function *(){
var parts = yield this.request.parts
var part
while (part = yield parts) {
// do something with the incoming part stream
part.pipe(someOtherStream)
}
console.log(parts.field.name) // form data
}
})``
But when I run my server I immediatly get the following error:
TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "undefined"
If I use co-busboy myself meaning:
var parts = parse(this) var part while (part = yield parts) { console.log (part); if (part.length) { // arrays are busboy fields console.log('key: ' + part[0]) console.log('value: ' + part[1]) } else { // otherwise, it's a stream part.pipe(fs.createWriteStream('some file.txt')) } }
It works amazingly perfect
I know it has something to do with generators but don't get why..
Also - am I supposed to validate the fields myself? because I can't do:
validate: { type: 'multipart', body: { username: Joi.string().required(), email: Joi.string().optional() } }
Din.
Hi,
I try to use ".allowedMethods()" (from koa-router) on a koa-joi-router instance, but it is undefined. The list of features includes "HTTP 405 and 501 support", is it really implemented ?
Thanks
[https://github.com/nodejs/package-maintenance/issues/77#issuecomment-463356844](Take a look at this comment) and this HN thread.
@alexmingoia has sold the koa-router repo to an unknown developer. This package depends on it.
this router is cool, but i want to add base url to the router so i can use it like this:
var pub = routuer({baseUrl:'/test'});
pub.post('/wow', ...);
then url '/test/wow' can be visited
can this be possible by some ways?
3q
Hey there!
Considering using koa-joi-router on our next few projects.
One issue I ran into is no ability to set options for Joi other than specifying the validation as Joi objects everytime and chaining an options call.
body: joi.object({ email: joi.string().email().required(), password: joi.string().required(), }).options({abortEarly: false}),
This is a bit tedious if you want something set for every validation everywhere.
F.ex. I'd like abortEarly: false to get all validation errors at once.
The way I see it, it could be done 2 ways:
Potential problems:
These "global" options overriding individual validations sent as Joi objects with options specified (like in the example above).
I'd be willing to tackle this and submit a PR if everyone's ok with the changes - already have solution 1. ready.
Multipart request not returning proper ctx.request.parts
object. returns FileStream
object without path key which contains file temp path and form-data key-values are also not returning. I tried async-busboy. works fine with the same request. It returns {files, fields}, if you say, I can take a chance to migrate from await-busboy to async-busboy.
like #44
handler: async (ctx) => {
const parts = ctx.request.parts;
const file = await parts;
const ext = file.filename.split('.').pop();
if (fileExt.indexOf(ext) === -1) {
ctx.status = 400;
return;
}
// passed in, but cannot get
const type = parts.field.type;
const types = ['sth'];
if (types.indexOf(type) === -1) {
ctx.status = 400;
return;
}
const filename = `sth`;
const result = await createBlockBlobFromStream(type, filename, file);
ctx.status = 200;
ctx.body = {status: 1, data: {filename}};
}
and i changed it to:
handler: async (ctx) => {
const parts = ctx.request.parts;
let part;
let file;
/* eslint no-cond-assign:0, no-await-in-loop:0 */
while (part = await parts) {
if (part.filename) {
file = part;
}
part.resume();
}
const ext = file.filename.split('.').pop();
if (fileExt.indexOf(ext) === -1) {
ctx.status = 400;
return;
}
// can get form-data fields
const type = parts.field.type;
const types = ['sth'];
if (types.indexOf(type) === -1) {
ctx.status = 400;
return;
}
const filename = `sth`;
// cannot upload to oss service, hanging without any err
const result = await createBlockBlobFromStream(type, filename, file);
// never print
console.log('123');
ctx.status = 200;
ctx.body = { status: 1, data: { filename } };
}
p.s.
exports.createBlockBlobFromStream = (container, filename, blob) => {
const deferred = {};
deferred.promise = new Promise((resolve, reject)=>{
deferred.resolve = resolve;
deferred.reject = reject;
});
blob.on('error', (err) => {
deferred.reject(err);
});
blob.pipe(blobService.createWriteStreamToBlockBlob(container, filename));
blob.on('end', () => {
deferred.resolve(1);
});
return deferred.promise;
};
As in joi document, the validate method receives 4 params.
validate(value, schema, [options], [callback])
Some options in validation is useful like language - overrides individual error messages. Defaults to no override ({}). Messages apply the following rules :
So will joi-router add options
support in the future when use the validate
method? ๐
How I can have sub router with prefix and add main router with another prefix?
userRoutes.js
import router from 'koa-joi-router'; const userRouter = router(); userRouter.prefix('/user'); const userRoutes = [...]; userRouter.route(userRoutes); export default userRouter;
mainRoutes.js
import router from 'koa-joi-router'; import userRouter from 'userRoutes'; apiRouter.prefix('/app'); apiRouter.route(userRouter.routes); export default apiRouter;
server.js
import AppApiRouter from 'mainRoutes'; app.use(AppApiRouter.middleware());
So actually, now my userRouter uri should be '/app/user/path'. Instead it is replacing prefix with new prefix string. In mainRoutes.js , apiRouter.route is directly accessing userRoutes.routes object. We are adding prefix as key in router object, that's why mainRouter is not able to get new path.
How to solve this?
It seems it would make sense to allow clients to request resources with either json, url encoded or form data. All three could be allowed by the API.
Currently the type
argument in the validation is only accepting a string.
I could create a PR that would accept an array too.
Currently date().greater
can not be used, cause the bundles joi version is too old.
Could you kindly update the dependencies?
handler: async (ctx) => {
const parts = ctx.request.parts;
let part;
try {
while ((part = await parts)) {
// handle files at the first place
part.pipe(someOtherStream);
}
} catch (err) {
}
// we need to use fields data first
console.log(parts.field.name);
}
we need to use fields data first, such as check username/password.
if the request is valid, then upload file to third party file storage service, like Azure-Blob-Storage
here is a solution that can work:
const Koa = require('koa');
const router = require('koa-joi-router');
const { Writable } = require('stream');
const { getDefer } = require('@dwing/common');
const handleFile = (part) => {
const deferred = getDefer();
const stream = new Writable();
stream.buffers = [];
stream.length = 0;
stream._write = function (chunk, encoding, next) {
this.length = this.length + chunk.length;
this.buffers.push(chunk);
next();
};
stream.once('finish', function () {
const buffer = (this.buffers.length === 0 ? new Buffer(0) : (this.buffers.length === 1 ? this.buffers[0] : Buffer.concat(this.buffers, this.length)));
deferred.resolve(buffer);
});
stream.once('error', (err) => {
deferred.reject(err);
});
part.pipe(stream);
return deferred.promise;
};
const multipart = async (parts) => {
const result = {
};
try {
let part;
// eslint-disable-next-line
while ((part = await parts)) {
if (part.length) {
result[part[0]] = part[1];
} else {
const field = part.fieldname;
result[field] = {
filename: part.filename,
mime: part.mime,
mimeType: part.mimeType,
encoding: part.encoding
};
const file = await handleFile(part);
result[field].length = file.length;
result[field].buffer = file;
}
}
} catch (err) {
// handle the error
}
return result;
};
const app = new Koa();
const routes = router();
routes.route([{
method: 'post',
path: '/upload',
validate: {
type: 'multipart',
maxBody: '5mb'
},
handler: async (ctx) => {
const parts = ctx.request.parts;
const body = {
...await multipart(parts),
...parts.field
};
console.log(body);
ctx.body = '1';
}
}]);
app.use(routes.middleware());
app.listen(3000);
but we wonder if there is a better way, that can copy filestream for later use..
Document says:
To run middleware before a specific route, also pass the optional path:
const router = require('koa-joi-router'); const users = router(); users.get('/:id', handler); users.use('/:id', runThisBeforeHandler);```
but runThisBeforeHandler
is actually run after handler
I can't install this package, with error: ( in China , it's OK in AWS us)
$ npm -v
5.4.2
$ npm install koa-joi-router
npm WARN deprecated [email protected]: XSS vulnerability fixed in v1.0.3
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/aheckmann/koa-router.git
npm ERR!
npm ERR! fatal: unable to access 'https://github.com/aheckmann/koa-router.git/': Unable to communicate securely with peer: requested domain name does not match the server's certificate.
npm ERR!
npm ERR! exited with error code: 128
By the way, I found the dependency version is very old compare to the original, can you upgrade it ?
And I wonder why not depend on the original koa-router
?
"koa-router": "git+https://github.com/aheckmann/koa-router.git#19e60d9"
https://github.com/aheckmann/koa-router
is now in version 5.1.2
.https://github.com/alexmingoia/koa-router
( the original ) is now 7.3.0
.ubuntu 16.04
Node.js 10.15.1
koa 2.7.0
koa-joi-router 5.2.0
'use strict'
const Koa = require('koa')
const app = new Koa()
const router = require('koa-joi-router')
const orders = router()
orders.get('/', ctx => {
ctx.body = 'Orders response'
})
orders.prefix('orders')
app.use(orders.middleware())
app.listen(3000)
localhost:3000/orders
returns 404 Not Found
localhost:3000/orders
returns 200 'Orders response`
Connected issue created in koa-router
Hello and thank you all for the work done on this project.
I'm trying to integrate this middleware into a project that already uses koa-bodyparser (required middleware to koa-passport).
Unfortunately when trying to reach a route when this middleware is declared the road never answers. I guess there is a conflict with co-body
somewhere.
Do we have a workaround for using this middleware with koa-bodyparser
or am i going wrong ?
const koa = require('koa');
const bodyParser = require('koa-bodyparser');
const router = require('koa-joi-router');
const Joi = router.Joi;
const app = koa();
const helloRouter = router();
app.use(bodyParser()); // <-- comment and the call will work
helloRouter.route({
method: 'post',
path: '/hello',
validate: {
body: {
name: Joi.string().required()
},
type: 'json'
},
handler: function* hello() {
const { name } = this.request.body;
this.body = `Hello ${name}`;
}
});
app.use(helloRouter.middleware());
app.listen(3000);
The call : curl -X POST -H "Content-Type: application/json" -d '{ "name": "John" }' "http://localhost:3000/hello"
const Koa = require('koa');
const router = require('koa-joi-router');
const Joi = require('joi');
const parse = require('co-body');
const routes = router();
routes.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'
},
handler: async (ctx) => {
console.log('sth'); // does not print
ctx.status = 200;
ctx.body = ctx.request.body;
}
}
]);
const app = new Koa();
// add this for test.
app.use(async (ctx, next) => {
ctx.request.body = await parse.form(ctx);
console.log(ctx.request.body);
// curl -d "name=test" http://localhost:3000/signup
// ^@curl: (52) Empty reply from server
// print: { name: 'test' }
await next(); // always pendding
});
app.use(routes.middleware());
app.listen(3000);
i need a pre-request-handler to check whether the token is valid.
so i have 3 questions:
The latest Joi version is 9
but this module uses a 1 year old version 6
. Any plans to update it?
Hello,
Great job on this project. I really like the declarative route specs you have created and some really good forethought went into exposing the routes for analysis and having a meta property.
I am building a number of micro-services and want some documentation to go along with them so I have created a small project that renders documentation from the route specs provided by koa-joi-router
. The project is koa-docs. I would appreciate a link from here so more people can use/contribute to it.
Thanks
Right now the only way to use the Joi middleware is to pass a configuration to the router. It would be more flexible to be able to use the middleware directly.
For example, instead of:
router.get('/user', config, handler)
It should be possible to do:
router.get('/user', joiMiddleware(config), handler)
This is more inline with a middleware based framework, allows the validation to be used after other middlewares, and enables wrapping and testing the validator.
I think this could be done in 2 ways. Either expose the middleware directly from the module, or extract the middleware to a joi-middleware
project on which joi-router
would depend. The second option is my personal favorite since it would also allow the middleware to be used with other routers. The first option however is definitely easier to implement.
Could you lint the production code?
Because now eslint checks only the test code.
You have to:
*.js
in this line https://github.com/koajs/joi-router/blob/master/package.json#L16.eslintrc
file in the main directorynpm run lint -- --fix
)Noticed that it's almost 10 months without any update. The major version of Joi is already 14 but that of the bundled one is just 10.
Anyway, great package with nice design!
when cors , options request response 404
I want to set joi allowUnknown
as a global config, because I don't mind what additional fields are passed in as long as the fields I want are supplied.
I have to set allowUnknown
in every Joi valid code, is there a way to set global Joi config like this below?
const router = require('koa-joi-router');
const pub = router({
validate: {
allowUnknown: true,
},
});
Hi! Thanks for your amazing work on this router!
I was wondering, if the router is able to execute a middleware before hitting a specifc path + method. In the documentation, I saw that is possible for a specific path but without method allocated to it.
var router = require('koa-joi-router');
var users = router();
users.get('/user/:id', handler);
users.use('/:id', runThisBeforeHandlerForGET);
users.post('/user', handler);
users.use('/:id', runThisBeforeHandlerForPOST);
Is it possible?
In the last commit, some changes have been made but it brokes the router. The dependency await-busboy
doesn't exist. I used Node v7.0.0 with --harmony
flag and I ran npm install
twice.
Thanks for the amazing work on this repository!
node --harmony server.js
module.js:474
throw err;
^
Error: Cannot find module 'await-busboy'
at Function.Module._resolveFilename (module.js:472:15)
at Function.Module._load (module.js:420:25)
at require (internal/module.js:20:19)
at Object.<anonymous> (/Users/aurelien/repositories/strapi/packages/strapi/node_modules/koa-joi-router/joi-router.js:9:16)
at Module._compile (module.js:573:32)
at Object.Module._extensions..js (module.js:582:10)
at Module.load (module.js:490:32)
at tryModuleLoad (module.js:449:12)
at Function.Module._load (module.js:441:3)
at require (internal/module.js:20:19)
Documentation: KoaRouter#param
That is, validate param input before KoaRouter calls the param middleware so that we can get the validated value in the middleware function.
router.route({
path: '/thing/:id',
handler: function* () {
// this.thing is set using router param middleware, below
this.body = this.thing
},
validate: {
params: {
id: Joi.number()
}
}
})
router.router.param('id', function* (id, next) {
// imagine db.getThing will throw if id is not a number
// currently this will fail if id is not a number since it is not yet validated
this.thing = yield db.getThing(id)
yield next
})
Using
validate: {
type: "form",
body: {
start: Joi.date().iso().greater("now"),
}
}
The form input of start="2018-10-10T10:00"
or start="2018-10-10T10:00Z"
both lead to
child "start" fails because ["start" must be a valid ISO 8601 date]
However Joi.validate("2018-10-10T10:00", Joi.date().iso().greater("now"))
=> error === null
Hello, genius, I found that it seems the query validator doesn't work. Here is my code:
const Koa = require('koa')
const app = new Koa()
const router = require('koa-joi-router')
const Joi = router.Joi
const p = router()
p.route({
path: '/',
method: 'get',
validate: {
query: {
t: Joi.string().min(3)
},
body: {
email: Joi.string().email()
},
type: 'json'
},
handler: async ctx => {
ctx.body = 'hello'
}
})
p.prefix('/api/v1/public')
app.use(p.middleware())
If the email is missed in the request body, it works very well. However, it doesn't throw any error without a t in the request query even with the email in the body. What's wrong?
If I get a multipart request that has a file field first with a subsequent text field, the second await
call will hang indefinitely.
If I have a text field first, the first await
will have the file stream and the second await
will still hang but the ctx.request.parts.field
array will be populated properly with the text fields.
See
7e7612e
How would one go about building an API where the version is not part of an URL?
Other popular methods include using HTTP Accept header, custom request headers, etc.
It seems like a good way to achieve this would be by introducing condition
to .route() options which can simply be a middeware function that returns a boolean. If the true it would continue to validate and execute handlers, if not it would simply skip to the next route(if any) with the same path and method.
This would give us an ability to inspect headers and execute the right validation and handlers based on the API version.
Thoughts?
NodeJS: 8.6.0
npm: 5.2.0
OS: Max OS X 10.12.2
koa: 2.4.1
koa-joi-router: 5.0.0
koa-bodyparser: 4.2.0
koa-body: 2.5.0
For the following app:
const koa = require('koa');
const router = require('koa-joi-router');
const bodyParser = require('koa-bodyparser');
const public = router();
public.route({
method: 'post',
path: '/foo',
validate: {
type: 'json',
},
handler: async (ctx) => {
ctx.status = 201;
}
});
const app = new koa();
app.use(bodyParser());
app.use(public.middleware());
app.listen(2999);
the request hangs. The same if koa-body
used instead if koa-bodyparser
.
I need a koa body parser to be able to use request body in the middlewares.
I wanted to validate a object in query string (?deactivated[from]=2019-05-23T08:18:45.283Z&deactivated[to]=2019-06-12T08:18:45.283Z
)
So I went ahead and tried:
query: {
deactivated: {
from: Joi.date(),
to: Joi.date(),
},
}
But the request just prints out ("deactivated[from]" is not allowed
).
I have discovered that ?deactivated={"from": "2019-01-01T08:18:45.283Z"}
works, but I would like the use the first approach, as that's how objects are converted to query string by default.
Is there any way I can get this behaviour?
From further testings, if I define the query like so:
query: {
"deactivated[from]": Joi.date(),
"deactivated[to]": Joi.date(),
}
It seems to work, but that's not a solution, the variable names are unusable.
The alias exists in koa-router but not implemented in joi-router. README.md is showing examples with .del() however.
function makeSpecExposer(spec) {
var defn = clone.clonePrototype(spec);
return function* specExposer(next) {
this.state.route = defn;
yield* next;
};
}
Why use clone to expose specs? It will produce some trouble like 'custom error will transform to undefined'.
When a route is matched, its path is available at ctx._matchedRoute and if named, the name is available at ctx._matchedRouteName
https://github.com/koajs/joi-router/blob/master/package.json#L44
OS: Max OS X 10.12.2
Node version: 8.6.0
npm version: 5.2.0
koa version: 2.4.1
koa-joi-router version: 5.0.0
joi version: 9.2.0
I have the following route:
module.exports = {
method: 'get',
path: '/',
validate: {
query: Joi.object().keys({
search: Joi.string(),
sort: Joi.object().default({ 'name': 1 }),
}),
},
handler: async (ctx) => {
const { sort } = ctx.query;
console.log(sort);
ctx.status = 200;
},
};
The actual behavior:
when I specify a query string ex. ?search=foo
, I get the correct default value for sort
parameter, however for no query string I get undefined
The expected behavior:
even for no query string, the sort
parameter should have { 'name': 1 }
as default value
No matter whether I set validate.type or not, the request body can't be parsed and can't get it in ctx.request
I try to package my node app with docker. I used node alpine for base image, of course for less container size, but node alpine does not contain git
to minimise image size and we are using git for aheckmann-koa-router package, so my docker built is throwing an error for alpine version
npm ERR! git clone --template=/root/.npm/_git-remotes/_templates --mirror https://github.com/aheckmann/koa-router.git /ro
ot/.npm/_git-remotes/git-https-github-com-aheckmann-koa-router-git-19e60d9-704f0691: undefined
npm ERR! Linux 4.4.0-72-generic
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "--production"
npm ERR! node v7.9.0
npm ERR! npm v4.2.0
npm ERR! code ENOGIT
npm ERR! not found: git
npm ERR!
npm ERR! Failed using git.
npm ERR! This is most likely not a problem with npm itself.
npm ERR! Please check if you have git installed and in your PATH.
I verified in my app, this is the only package using git for koa-router, so it will be better if we use it npm package so that we can reduce the container size from 83MB to 14MB.
related #39
We are still using version 4.0.0 of this package which has a specific commit dependency on your fork of koa-router:
git+https://github.com/aheckmann/koa-router.git#f0c60d374b5903f
This has started breaking in our recent builds. Is it possible you changed something on your fork that would break this?
When we try and npm install we get a git error:
npm info git [ 'rev-list', '-n1', 'f0c60d374b5903f' ]
npm ERR! git rev-list -n1 f0c60d374b5903f: fatal: ambiguous argument 'f0c60d374b5903f': unknown revision or path not in the working tree.
We can see that commit in github, but it does not seem to appear in any branch?
Hi,
First, thanks for the awesome package, i just begin to use it.
I make separation between routes, controllers and my handlers.
All my GET routes of my crud works fine, but when i want to make validation on my POST and my PUT routes, it doesn't work.
If i comment validate{}, in my route, it works. I think it's joi. But in the top of my file i have:
var router = require('koa-joi-router');
var Joi = router.Joi;
You can see a simple exemple of the skeleton of my API. The real project have a complete CRUD, if you want to see it, i can share the other pages. I separate my routes definitions in 3 parts, the route.js file :
var router = require('koa-joi-router');
// Controllers
var messageCntrl = require('./api/controllers/message');
module.exports = function(app) {
var general = router();
general.prefix('/api');
general.route(messageCntrl.post);
// others routes
}
The controller:
var router = require('koa-joi-router');
var Joi = router.Joi;
// Handlers
var messageHndlr = require('../handlers/message');
var ctrl = module.exports = {};
// Need to fix
ctrl.post = {
method: 'post',
path: '/message',
validate: {
body: {
name: Joi.string().max(100)
},
type: 'json',
},
handler: messageHndlr.post
};
The handler
var hdlr = module.exports = {};
hdlr.post = function *(next){
yield next;
var error, request, result;
// console.log(this.request.body);
console.log('dans le handler');
try {
var request = new Message(this.request.body);
result = yield request.save();
this.status = 200;
return this.body = result;
} catch (error) {
// console.log(error);
return boom.wrap(error, 400);
}
};
You can see screen if you want but i don't have a lot of other informations:
With validate in my controller (just loading long time and show this):
When i comment validate in my controller:
It doesn't works for body validate not for params, and i didn't test for headers. I expose POST route but it's exactly the same for my PUT route.
Thanks for your help
If a query parameter is validated as a Joi date object, the request will pass validation but the param will be an empty string once inside the route.
I believe this is due to the validateInput method relying on setting request['query'] to the validated Joi object. Unfortunately, this.request.query has getter/setter logic inside of Koa:
get query() {
var str = this.querystring;
var c = this._querycache = this._querycache || {};
return c[str] || (c[str] = qs.parse(str));
},
set query(obj) {
this.querystring = qs.stringify(obj);
},
The properly validated objects are being stringified by koa, and then parsed again when accessing this.request.query inside the router. It looks like boolean params also turn into strings rather than primitives which is likely the same issue.
I would like to use my own middleware on the routes. I know how to define it from the documentation, and the middleware is hit, but I would like to access the current route definition in it. Similar to how joi-router adds the validate property to the route definition, I would like to add my own (roles).
How can I access the current route definition from my middleware?
It appears the header, query, params, and body properties of the validate object in route() options do not support a Joi schema object as generated by Joi.compile(). Any plans to support Joi schema objects in these cases? Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.