1

We used the example from the moleculer website as the basis for our API gateway, and are a having issues when routes throw errors - the onError handler is never hit, the exception is unhandled and node crashes the app. Not the idea!

I realise this is NOT a complete example but a quick eye cast over if we have made any gross errors of concept, or if we should expect the onError handler to be hit would be nice...

const OpenApiMixin = require('./openapi.mixin')
const { MoleculerError } = require('moleculer').Errors

class BadRequestError extends MoleculerError {
  constructor (message) {
    message = message || 'Bad request'
    super(message, 400, 'Bad request')
  }
}

...
const functionThatCanThrowError = async (req, res)=>{
    if (!req.body.email) {
      throw new BadRequestError('No email transferred.')
    }
    ...
}

module.exports = {
  name: 'api-gateway',
  mixins: [ApiGateway, OpenApiMixin()],
  settings: {
    ...
    path: '/',
    routes: [
    {
      path: '/api',
      ...
      aliases: {
            'POST /route-can-throw-error': functionThatCanThrowError
      },

      // Route error handler
      onError (req, res, err) {
        let { type, code, message, data, name } = err
        res.writeHead(Number(code) || 500, { 'Content-Type': 'application/json' })
        res.end(JSON.stringify({ type, code, message, data, name }))
      }
    }
 ]
}``
kpollock
  • 3,899
  • 9
  • 42
  • 61

2 Answers2

4

The defined functionThatCanThrowError is a middleware. It should be an Express-like middleware where you can't throw an error. To do it, you should call next(err).

E.g:

const functionThatCanThrowError = async (req, res, next)=>{
    if (!req.body.email) {
      next(new BadRequestError('No email transferred.'))
    }
    ...
}

More info: https://expressjs.com/en/guide/error-handling.html

Icebob
  • 1,132
  • 7
  • 14
  • Ah, interesting, Thank you. I never thought of the functions that we execute on our routes as middleware, or associated that with the relevant part of the documentation. We will have to do a bit of a rewrite, but it is good to know why. One question, to further my understanding. What kind of errors are the "onError" route and global error handlers *supposed* to handle? – kpollock Feb 10 '20 at 07:52
  • Should I also return from the function after the "next", or how else to abort/end execution of this function correctly at this point, so that the error is immediately reported to the calling code in the api-gateway? In reality, the function makes a few other checks then executes an http request, if all is well. – kpollock Feb 10 '20 at 08:27
  • The `onError` can catch errors from services (action calls). – Icebob Feb 10 '20 at 13:33
  • If you don't call the `next` it will break the chain and you should return with the correct response like this: ```js const functionThatCanThrowError = async (req, res, next)=>{ if (!req.body.email) { res.writeHead(400); res.end("No email"); return; } next(); } ``` – Icebob Feb 10 '20 at 13:35
3

What @icebob said + an example


    module.exports = {
        name: "api",
        mixins: [ApiGateway],
        settings: {
            port: process.env.PORT || 3000,
            routes: [
                {
                    path: "/api",

                    whitelist: ["**"],

                    aliases: {
                        "GET /req-res-error": [
                            (req, res, next) => {
                                next(new MoleculerError("Req-Res Error"));
                            }
                        ],
                        "GET /action-error": "api.actionError"
                    },

                    onError(req, res, err) {
                        this.logger.error("An Error Occurred!");
                        res.end("Global error: " + err.message);
                    }
                }
            ]
        },

        actions: {
            async actionError() {
                throw new MoleculerError("ACTION ERROR");
            }
        }
    };

André Mazayev
  • 393
  • 2
  • 10