const Koa = require('koa')
const app = new Koa()
app.use(async ctx => {
ctx.throw('aaa')
})
app.listen(4000)
This will throw a real javascript runtime error, and gives requester a response InternalServerError: aaa.
So, the code below looks weird:
try {
const {username, password} = ctx.request.body
const user = getUserFromDB(username)
if (user.password === password) {
ctx.status = 200
} else {
ctx.throw(401, 'Invalid username or password')
// this information should return to requester directly, but in fact, it was caught by below `catch`, and return a 500 error to the requester.
}
} catch (e) {
ctx.throw(500, 'some other code error')
}
And there is anther situation:
When I use supertest to test my server, when run into ctx.throw(500, 'blabla'), it will shows on test result , it is very ugly...

I known this is cause by I use the koa instance in my test case, but any other best practice? Thanks.
I think , ctx.throw not throw a real javascript runtime error will help above two situation
In your example, the try/catch block is working overtime. I prefer to try/catch single operations that can throw, and perform any other operations that throw in their own block. Your code in this format would look more like:
const {username, password} = ctx.request.body
let user;
try {
user = getUserFromDB(username)
} catch (e) {
ctx.throw(500, 'some other code error')
}
if (user.password === password) {
ctx.status = 200
} else {
ctx.throw(401, 'Invalid username or password')
// this information now returns to requester directly
}
Also, I'm going to assume this is pseudocode, but I do have a moral obligation to point out that if(user.password === password) implies storing passwords as plaintext, and that's a bit scary ;-)
Thank you!!! I got it. I did use try...catch overtime. This helps me a lot.
But what about ctx.throw throw a read runtime error? It seems no necessary.
As storing password plaintext, this is a toy web server, I think it's all right. Thanks for your reminder.
ctx.throw() throws an error to let the error event handler take control, which by default is set by Koa in order to gracefully respond (which includes _any_ thrown error).
The ctx.throw() method is a convenience that will set err.status to a corresponded HTTP status code (defaulting to 500), as well as set some other properties. See this doc for more info.
I means we don't need extends error object to handle some error that we want.
But at latest, I think those two have same behavior, so, use Error object is fine.
Thanks for your patience, I'm very appreciate.
I am having a similar problem understanding what is going on. If I dont set ctx.status but only ctx.throw(422), inside the public_error middleware ctx.status is 404. So in order that to work, I need to both set ctx.status = 422 and ctx.throw(422)
why is that? Should I set the ctx.status before ctx.throw everytime?
async function public_error(ctx: Koa.Context, next: Function) {
try {
return await next()
} catch (err) {
console.log(err)
console.log(ctx.status)
switch (ctx.status) {
case 422:
ctx.status = 422
// simplified for brevity
ctx.body = { error: err.message }
break;
default:
// send stack and stuff to sentry
ctx.status = 500
ctx.body = { error: 'something else went wrong' }
ctx.app.emit('error', err, ctx);
}
}
}
async function validate_signup_request(ctx: Koa.Context, next: Function) {
console.log("validating signup request")
var result = validate(ctx.request.body);
if(!result){
ctx_log.warn(ctx, "Invalid signup request", validate.errors);
//ctx.status=422;
ctx.throw(422, "Invalid signup request");
}
else{
await next();
}
}
@mustafaekim, ctx.status is not set because you're using a custom error handler public_error instead of the default Koa handler. ctx.throw() will set err.status for you, per ctx.throw docs, and the default Koa handler is what sets the properties on ctx.
If you want to let Koa do its thing, you can respond to error events with app.on('error', ....) per this section.
Hope that helps!
The middlewares are registered in the following order:
app.use(public_error())
/* Routers */
app.use(users_router.routes());
app.use(guest_router.routes());
/* Error handling */
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
So to my understanding, if any function in any route calls ctx.throw, firstly public_error should catch. But when it does, it observes that the ctx.status is not what I've thrown but 500. So there is some steps that I do not observe, which sets the ctx.status to 500. Where is it?
That's why inside the function I should both throw 422 and set the status to 422. If I set the status to 422, this hidden function does not reset the status to 500. I could not understand the flow
So to my understanding, if any function in any route calls ctx.throw, firstly public_error should catch.
Koa executes middleware in a stack, so the most recent registered middleware that has a try/catch block around the next() call will respond first. The other try/catch blocks won't catch unless you throw the error from the current catch block.
Since Koa's built in error handler is what sets ctx.status etc., and it is registered first, your handler will actually catch the error before Koa has a chance to respond. Your custom handler is essentially duplicating Koa's built-in handler, so you probably can just remove public_error altogether and do what you need to do in the 'error' event listener.
For others stumbling upon this, if you are setting your code to a non-official HTTP status code, Koa will sneakily set it to 500: https://github.com/koajs/koa/blob/d48d88ee17b780c02123e6d657274cab456e943e/lib/context.js#L150
This tripped me up for a bit.
Most helpful comment
ctx.throw()throws an error to let the error event handler take control, which by default is set by Koa in order to gracefully respond (which includes _any_ thrown error).The
ctx.throw()method is a convenience that will seterr.statusto a corresponded HTTP status code (defaulting to 500), as well as set some other properties. See this doc for more info.