Masking GraphQL Errors
The way **graphql-js** and **express-graphql** handle errors is has some issues. Consider this example server which has a bug in a resolve function.
The way graphql-js and express-graphql handle errors is has some issues. Consider this example server which has a bug in a resolve function.
When the query “{ users }” is executed, the server will respond with the error message “User.find is not a function”. There are several problems with this behavior:
[ 1 ] Error stacktraces are hidden
When developing the application or when running it in production error stack traces are extremely useful to pinpoint issues. But when errors occur inside resolve functions, they are not logged on the server.
[ 2 ] Server source code get exposed
Error messages may have parts of the source code in it. When running the above example server, with the error message alone it is visible that the server program tries to call a find method on a variable called User. With this knowledge, it's possible to make good assumptions about the server.
[ 3 ] Users get cryptic error messages
Error messages automatically sent to the user are often not consumer friendly. It would have been more useful if the user received an error message like “An unexpected error has occurred. Please report to [email protected]” instead of “User.find is not a function”.
To avoid these issues, errors happening inside resolve functions must be processed before sending them to the client. Unless changes are made to graphql-js these errors must be handled inside resolve functions or after completely processing the query. Both approaches have limitations.
There are quite a few open issues on the graphql-js Github repo related to error handling, most of them can be handled by:
- Logging all error messages and stacks on the server.
- Allowing the user to control errors sent to the client.
Enter graphql-errors
The graphql-errors node module attempts to solve these issue by masking errors sent to users and logging errors on the server. Instead of sending the real error message, it sends a simple error message “Internal Error”. This protects the server source code from leaking out. It also logs error messages on the server with stacktraces.
Fields, types or schemas can be processed to behave this way using the maskErrors function. If it's necessary to handle errors in a different way, it can be done easily using the setDefaultHandler function or by giving an error handler function as the second argument for maskErrors.
Custom Error Messages
It is also a good practice to send user friendly error messages to the client. This can be done by using a special UserError type. When a UserError is thrown inside a resolve function, it will be sent to the user.
For the Meteor GraphQL support, we are using a slightly different approach and using Meteor's Meteor.Error instead of UserError.
You can find the graphql-errors module on Github (kadirahq/graphql-errors) with more information. If you wish to join the discussion about whether custom error types should be a part of GraphQL, checkout this Github Issue.