0

Using Koa v2.7.0 and TypeScript v3.3.1

My "errorHandler" middleware function being exported like this

export const clientErrorHandler = () => {
    return async (ctx: Context, next: NextFunction) => {
        try{
            await next();
        }
        catch (err){
            if(err instanceof HTTPClientError){
                console.warn(err);
                ctx.status = err.statusCode;
                ctx.body = err.message;
                ctx.app.emit('error', err, ctx);
           }
       }
   }
}

My logic which handles the attachment of the middleware on the given Koa app looks like this:

export const handleErrors = (app: Koa) => {
    const serverErrHandler = serverErrorHandler();
    serverErrHandler._name = 'serverErrorHandler';
    app.use(serverErrHandler)
}

The reason why I'm trying to create this property is due to this Koa documentation (see link below) which states that we can give middleware functions a _name property so that when we run the program with DEBUG=koa* set, this middleware function will be able to have a name that shows up in the console.

Since JavaScript does not allow defining function names at runtime, you can also set a middleware's name as ._name. This is useful when you don't have control of a middleware's name. For example:

const path = require('path'); const serve = require('koa-static');
const publicFiles = serve(path.join(__dirname, 'public'));
publicFiles._name = 'static /public';
app.use(publicFiles);

Source of the above snippet: https://github.com/koajs/koa/blob/master/docs/guide.md#debugging-koa

However, when trying this, since I'm using TypeScript, it does not like it that I'm trying to set a property on this anonymous function.

[ts] Property '_name' does not exist on type '(ctx: Context, next: NextFunction) => Promise'.

I was hoping to determine the best way to go about enabling myself to be able to add this little _name property to this anonymous function so that I could have solid debugging logs.

CaffeinateOften
  • 571
  • 1
  • 6
  • 19

1 Answers1

0

As I was writing this out I ended up finding two possible solutions.

The one that I'm going with, is to simply not use an anonymous function, so there is no need to modify a _name property on my middleware function. After re-reading the koa documentation I think a key phrase I looked over was

"for middleware that you don't have control over"

My middleware function definition now looks like this:

export const clientErrorHandler = () => {
    let clientErrHandler = async (ctx: Context, next: NextFunction) => {
        try{
          await next();
        }
        catch (err){
            if(err instanceof HTTPClientError){
              console.warn(err);
              ctx.status = err.statusCode;
              ctx.body = err.message;
              ctx.app.emit('error', err, ctx);
            }
        }
      }
    return clientErrHandler;
}

Now, if this hypothetically were the case where you didn't have control over a 3rd party middleware function name AND you were using TypeScript, I found that this stack overflow solution helps in that scenario: https://stackoverflow.com/a/18640025

Using that that stackoverflow's suggestion, one could define an interface and do a type assertion with the anonymous function to produce a result that would let the caller typescript code set the _name property on the returned function without complaining that no such property existed.

Just to demonstrate what this looks like:

Even though I'm demonstrating this on the code that you technically wouldn't have access to modify I assume you could do a type assertion on the caller side as well and then modify the _name property after that.

interface MiddlewareFunction { (ctx: Context, next: NextFunction ): Promise<void>; _name: string; }

export const serverErrorHandler = () => {
  return <MiddlewareFunction> async function (ctx: Context, next: NextFunction){
    try {
      await next();
    }
    catch (err) {
      if(process.env.NODE_ENV === 'production'){
        ctx.status = 500;
        ctx.body = 'Internal Server Error';
      }
      else {
        ctx.status = err.status;
        ctx.body = err.stack;
        ctx.app.emit('error', err, ctx);
      }
    }
  }
}

Same code as in the question:

export const handleErrors = (app: Koa) => {
    const serverErrHandler = serverErrorHandler();
    serverErrHandler._name = 'serverErrorHandler';
    app.use(serverErrHandler)
}

EDIT: You can also just change from arrow function back to a normal named javascript function in the return.

export const serverErrorHandler = () => {
  return async function serverErrorHandler (ctx: Context, next: NextFunction){
    try {
      await next();
    }
    catch (err) {
      if(process.env.NODE_ENV === 'production'){
        ctx.status = 500;
        ctx.body = 'Internal Server Error';
      }
      else {
        ctx.status = err.status;
        ctx.body = err.stack;
        ctx.app.emit('error', err, ctx);
      }
    }
  }
}
CaffeinateOften
  • 571
  • 1
  • 6
  • 19