Koa: Koa response 404 even though is succesful

Created on 20 Feb 2017  路  10Comments  路  Source: koajs/koa

I am making an api with Koa:

This is the router handler: (assume I have router.get(..., get) and app.use(router.routes())

async function get(ctx, next){
  console.log(ctx.response)
  let user = await getUser(1);

  ctx.body = {
    data: user || [],
    message: ctx.response.status
  }
}

The api returns the user but I've noticed that even tough it matches the route, the data is returned as JSON and on the browser the response is 200, when I log the response is 404.

Is this a bug or more configuration is needed?

I am using Koa 2 (@next)

Most helpful comment

Figured this out from another thread. Basically, you need to search all your middleware functions and make sure that they are all async functions(Yes! Every single one of them attached to Koa).

All 10 comments

you should log after ctx.body=, this line will change the response's status from 404 to 200

@dead-horse How do you get the response code to be returned on the api? thanks

async function get(ctx, next){
  let user = await getUser(1);

  ctx.body = {
    data: user || [],
    message: ctx.response.status
  };

  console.log('the response status is ', ctx.status);
}

@dead-horse I am having this same issue

   router.post("/send", async ctx => {
      const ctl = require("./controllers/Messages");
      ctx.body = await ctl.send(ctx);
    });

//controllers/Messages.js
this.send = async ctx => {
      return await emailService.sendEmail(ctx.request.body);
  };

It returns 400. And if I add a log(console.dir(ctx.body)) after the ctx.body = await ctl.send(ctx); line, it outputs the resolved value from the async call.

Please help

Figured this out from another thread. Basically, you need to search all your middleware functions and make sure that they are all async functions(Yes! Every single one of them attached to Koa).

In addition to the above answer by @Oyelaking, make sure that none of your middlewares are prematurely returning without data.

In my case, I was calling jwt.verify without returning anything.

module.exports = async (ctx, next) => {
  const token = ctx.cookies.get('token');

  jwt.verify(token, config.auth.jwtSecret, (err, payload) => {
    if (err) {
      ctx.throw(401, {
        data: {
          error: 'UNAUTHORIZED',
          message: 'Invalid token.'
        }
      });
    } else {
      return next();
    }
  });
}

As you can see, I'm just calling jwt.verify() and I only call next() within a callback. This gets separated onto its own 'thread' and the middleware returns prematurely with no data on ctx.body.

I solved it by calling jwt.verify() with async/await.

  try {
    await jwt.verify(token, config.auth.jwtSecret);
    return next();
  } catch(err) {
    ctx.throw(401, {
      data: {
        error: 'UNAUTHORIZED',
        message: 'Invalid token.'
      }
    });
  }

@jemhuntr Thanks very much, this was messing with my head 馃様

I had a similar issue where I forgot to await my next() call:

.get('/*', async (ctx, next) => {
    validate(ctx.request.body);
    next(); // Should be await next();
})
.get('/some-route', async (ctx, next) => {
    ctx.response.body = 'some ok response';
    // Code here will execute, but have no influence on the response as it has already 'returned'.
    // Based on the route above, all subsequent routes will return 404s.
})

Figured this out from another thread. Basically, you need to search all your middleware functions and make sure that they are all async functions(Yes! Every single one of them attached to Koa).

Thanks for your guidance!鉂わ笍

But if I've attached my middleware to a route like this
because I want to use this middleware only this route!

const validationMw = (dtoClass: any) => {
  return async function (ctx: RouterContext, next: () => Promise<any>): Promise<void> {
    const transformedData = plainToClass(dtoClass, ctx.request.body)

    try {
      await validateOrReject(transformedData)
      await next()
    } catch (errors) {
      if (errors.length > 0) {
        let errorTexts = <any>[]
        for (const errorItem of errors) {
          errorTexts = errorTexts.concat(errorItem.constraints)
        }
        httpBadRequest(ctx, errors[0].constraints)
      }
    }
  }
}

// Route
router.post('/assets', validationMw(AssetRequestDto), createAssetController)

What should I do !?

This problem can also be solved by specifying the middleware in this fashion:
app.use(async (ctx) => (ctx.body = 'Hello'));

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ElegantScripting picture ElegantScripting  路  5Comments

rainesinternationaldev picture rainesinternationaldev  路  5Comments

koalex picture koalex  路  3Comments

usernameisalreadytaken2014 picture usernameisalreadytaken2014  路  4Comments

dounine picture dounine  路  4Comments