moleculerjs/moleculer-apollo-server

Logging Graphql Errors Along with Metadata from 'req' Object

Opened this issue · 2 comments

I'm looking for some direction on how to log graphql related errors while also adding relevant information from the req object. I'm able to log the error object using the serverOptions.formatError function but unable to access the req object from here.

      serverOptions: {
        formatError(err) {
          logger.error({ err }); // looking to add userID here which is available from jwt token in request object
          return err;
        },
      },

I don't believe the following snippet from service.js ever actually triggers the this.sendError function:

   try {
      await this.prepareGraphQLSchema();
      return this.graphqlHandler(req, res);
   } catch (err) {
      this.sendError(req, res, err);
   }

...because moleculerApollo.js swallows the error by returning undefined in the catch handler:

	try {
		const { graphqlResponse, responseInit } = await runHttpQuery([req, res], {
			method: req.method,
			options,
			query,
			request: convertNodeHttpToRequest(req),
		});

		setHeaders(res, responseInit.headers);

		return graphqlResponse;
	} catch (error) {
		if ("HttpQueryError" === error.name && error.headers) {
			setHeaders(res, error.headers);
		}

		if (!error.statusCode) {
			error.statusCode = 500;
		}

		res.statusCode = error.statusCode || error.code || 500;
		res.end(error.message);

		return undefined;
	}

I think the issue here is that this.graphqlHandler is an async function. By returning without awaiting the catch block for this.sendError gets bypassed. I think this is fixed with return await this.graphqlHandler which should allow the catch block to remain in scope and be able to hit the this.sendError.

Can you try out that change and see if it does the trick for you?

I think that's part of the solution. The other issue is that the this.graphqlHandlerfunction in moleculerApollo.js has a try/catch block that doesn't rethrow the error and instead returns undefined when an error is caught. this.sendError is never triggered in service.js.

	try {
		const { graphqlResponse, responseInit } = await runHttpQuery([req, res], {
			method: req.method,
			options,
			query,
			request: convertNodeHttpToRequest(req),
		});

		setHeaders(res, responseInit.headers);

		return graphqlResponse;
	} catch (error) {
		if ("HttpQueryError" === error.name && error.headers) {
			setHeaders(res, error.headers);
		}

		if (!error.statusCode) {
			error.statusCode = 500;
		}

		res.statusCode = error.statusCode || error.code || 500;
		res.end(error.message);

		return undefined;
	}