graphql-tools: Stitching with formatError is very limited...

If you want to format your errors when using stitching, you can’t tell the error type from the error.

The issue seems to be located here:

https://github.com/apollographql/graphql-tools/blob/master/src/stitching/errors.ts#L80

So packages like apollo-errors don’t work because you can’t get the type from it.

One solution I think I see that’s somewhat possible is to check if it’s a schema created for a remote server and if so, run checkResultAndHandleErrors, otherwise, just throw the error as is since checkResultAndHandleErrors looks to be made for remote responses, and if it’s local, the errors should just be thrown normally

https://github.com/apollographql/graphql-tools/blob/9ac0aeb25cd6c99d2e1ce6c14809d64f85b87eaf/src/stitching/mergeSchemas.ts#L322-L330

Thoughts?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 19
  • Comments: 37 (3 by maintainers)

Most upvoted comments

In my case, I have micro-services connected to an api gateway that makes the stitching. The orignal error was deep buried: error.orignalError.errors[0].originalError.errors[0] Here is how I solved the issue:

In User micro-service directrives/auth.js

const {
  SchemaDirectiveVisitor,
  AuthenticationError,
  ForbiddenError
} = require('apollo-server-express');

// ...
  if (!context.token) {
    throw new AuthenticationError('User not authenticated');
  }
// ...

In Api Gateway helpers/searchOriginalError.js

function searchOriginalError (error) {
  if (error.originalError) {
    return searchOriginalError(error.originalError);
  }
  if (error.errors) {
    return error.errors.map(searchOriginalError)[0];
  }
  return error;
}

module.exports = { searchOriginalError };

In Api Gateway index.js

const server = new ApolloServer({
  schema,
  context: ({ req }) => {
    // ...
  },
  formatError: (error) => {
    const originalError = searchOriginalError(error);
    logger.error(originalError);
    return originalError;
  }
});

I hope, this helps.

Unfortunately @OlivierCuyp’s solution didn’t work in our case either. The upstream service errors weren’t being exposed to formatError.

This flow, (flow 1) makeRemoteExecutableSchema => createResolver => checkResultsAndHandleErrors raised the upstream error.

However the subsequent (flow 2) MergeSchemas => delegateToSchema => applies CheckResultsAndHandleErrors as transform raised an error that had lost the upstream error context.

Narrowed it down to this line in checkResultAndHandleErrors. When this error is thrown, it’s received by the mergeSchemas flow but has lost the real error context.

Workaround was to add an error handler to the httpLink that appended an additional error so that a CombinedError got thrown from checkResultAndHandleErrors like so

const errorLink = onError(({ graphQLErrors, response }) => {
  if (graphQLErrors && graphQLErrors.length === 1) {
    response.errors = graphQLErrors.concat(new ApolloError("hax0r"));
  }
});

Then flow 2 received an error with the right context that was then conveyed to formatError.

Why wouldn’t stitched errors be appended, unaltered, to the errors array? Let the stitched schema dictate its errors’ format.

I am still having issues viewing any extra properties associated with an error inside formatError:

    const e = new Error('An Error...');
    e.data = 'Error data...';
    throw e;

Inside formatError I would expect to find the data property but I don’t:

export const formatError = error => {
    const {originalError} = error;
    console.log(originalError.errors[0]);
    return error;
};

output:

{ Error: An Error...
    at _callee$ (/Users/davidstummer/code/DeliveriesOnDemand/ServiceLayer/app/admin/creditor/resolvers.js:12:15)
    ...
 message: 'An Error...',
  locations: [],
  path: [ 'pageCreditors' ] }

This only occurs when using mergeSchemas

Update: I managed to finally find my error object here:

error.originalError.errors[0].originalError

This is also impacting us.

We were unable to pass custom error codes because the merged schema swallows the thrown errors.

Thanks @brycesteinhoff, I’ve got something working based on @DavidStummer’s solution.

Posting it here for the next person who faces this issue 😃

// this is the formatError function for server which is stitching the schemas together
formatError: (error) => error.originalError.errors[0].originalError

I’m having an issue with this also.

I’ve created remote executable schemas and merged them with mergeSchemas. I’m trying to use formatError function when executing against the merged schema, but the error passed as an argument only has the message, locations, and path properties, not other attributes of the error returned from the remote schema.

Is there a way to make properties of errors on remote schemas available to the merged schema? Or does the logic in mergeSchemas swallow them?

For us, using formatError and simply returning the error with NO changes impacts behavior. That’s silly right? E.g.

formatError: (error) => error

This ^ behaves differently than no formatError

We are using schema stitching… it seems like data is stripped or removed from the errors only if we have a formatError function defined.

I ran into this today. Needs a fix. Very hard to debug.

I had a subscription field returning a list which was returning this in graphql: [null, null, null].

The issue was one of the fields in the list items missing. There is no way to see the error without turning off stitching.

This should be labelled as bug, not enhancement.

@lakshyaranganath, it seems this issue is actually with makeRemoteExecutableSchema, not mergeSchemas only. The workaround I’ve come up with for now is to intercept GraphQL errors on the remote schema, serialize the properties all into the error message as JSON, then format the errors by deserializing back to properties.