javascriptnode.jskoakoa2

Proper usage of next() in Koa.js Middleware


I use Koa (2.11.0).

I have this very much simplified middleware function, which looks strange after simplification.

However, it is easier to ask the question. I have two if statements, and I throw three times. After throwing, I am unsure what would be the best thing to do in all three places:

  1. do nothing, Koa will handle all
  2. add return, while it makes no value adding it, at least some readability to the code.
  3. add return next()
//This is Koa middleware
const tokenProvider: (persons: string[]) => Middleware = (
): Middleware => {
  return async (ctx: Context, next: () => Promise<void>): Promise<void> => {
    const authorizationHeader: string = ctx.headers.authorization

    if (!authorizationHeader) {
      ctx.throw(STATUS_CODES.UNAUTHORIZED)
    }
    try {
      const token: string = authorizationHeader.split(' ').pop()
      if (!token ) {
        ctx.throw(STATUS_CODES.FORBIDDEN)
        // Should I use:
        // - return
        // - return next()
        // - nothing should be used, as Koa will handle it
      }

      return next()
    } catch (error) {
      ctx.throw(STATUS_CODES.UNAUTHORIZED)
    }
  }
}

Solution

  • Koa's ctx.throw actually throws an exception, so there's no point in putting statements immediately after ctx.throw as they will be skipped, just as they would with throw new Error().

    This also means that this statement isn't useful:

    ctx.throw(STATUS_CODES.FORBIDDEN)
    

    Because you immediately catch it and turn it into something else after. This is how I would rewrite your middleware:

    //This is Koa middleware
    const tokenProvider: (persons: string[]) => Middleware = (
    ): Middleware => {
      return async (ctx: Context, next: () => Promise<void>): Promise<void> => {
    
        const authorizationHeader: string = ctx.headers.authorization
    
        if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) {
          ctx.set('WWW-Authenticate: Bearer');
          ctx.throw(
            STATUS_CODES.UNAUTHORIZED,
            'Authentication with a Bearer token is required on this endpoint',
          );
        }
        const token = authorizationHeader.substr('Bearer '.length);
        // I supposed you want check the bearer token here? 
        return next()
      }
    }
    

    Note that I assume you're using Bearer tokens, but if you're using something else you can substitute this.