GraphQL Errors: How Do They Work, And What Can I Do About Them?
GraphQL adds an errors list alongside the data in a response body
GraphQL is a wonderful technology, but it can be incredibly frustrating trying to understand its error handling, both in philosophy and implementation.
I shall attempt to, gasp, explain both.
I’ll also go over Apollo’s current solution to handling errors. After all, they’re the eight hundred pound gorilla in the GraphQL room.
By the end of this article, I hope you will achieve networking Nirvana. Or at least know a bit more about GraphQL errors.
Philosophy
Let’s say you are planning a wedding. The REST way to do this would be for you to do all the calls, negotiating, booking, hair-pulling, and scheduling with each individual vendor.
The GraphQL way would be to tell a wedding planner everything you want for your wedding. They run off and do the heavy lifting.
If the wedding planner can’t get you the flowers you asked for, you don’t cancel the entire wedding. The wedding, like the show, must go on.
This is GraphQL’s philosophy regarding errors. A client sends a single, custom request to a server. The server executes this by sending off many requests of its own. The server then gathers up the resulting data (and any errors incurred along the way) and sends them back to the client.
Implementation
According to the GraphQL spec, a response object should contain at most two keys: “data” and “errors.” All data is placed in the data object, and all errors are placed in the errors list. (Actually, there’s a third key, “extensions.” More on that later with Apollo.)
A query has three phases (parsing, validation, and execution). Where the error occurs determines if any data can be returned to the client or if the client only receives an errors list.
The parsing phase is where the server transforms the query into an abstract syntax tree (AST). Errors at this stage would be syntax errors, meaning the client sent a malformed query.
The validation phase is where the server checks the AST to ensure it is a valid query (e.g. the client didn’t specify an argument as a string when it was supposed to be an integer).
The execution phase involves the server walking through the AST in a breadth-first manner, calling resolve functions, collecting results, and returning the data.
You write error handling inside your resolve functions. These are the functions that fetch the individual fields in your query. Basically, if an error is thrown outside of a resolve function, that means the entire query is rejected. The client will receive a response object that only has an errors list. These types of errors are server errors and client issues during the parse and validation phase.
If an error is thrown inside of a resolve function, the client is able to receive some data, depending on if any of the other resolve functions successfully returned data. These types of errors occur during the execution phase of a query.
Apollo’s Solution
Apollo distinguishes between the two main types of errors by labeling them either “networkError” (an error thrown outside of a resolve function) or “graphQLError” (an error thrown inside a resolve function).
Apollo adds an “extensions” key to each error item with a stack trace and a human-readable string under the “code” key that allows the server to categorize errors. Some of these error codes Apollo provides are AuthenticationError, ForbiddenError, and UserInputError.
For example, your application could attempt to refresh an expired authentication token by handling the AuthenticationError rather than ending a user’s session.
Conclusion
I hope that you take away three things from this article:
- The philosophy of GraphQL is to facilitate sending custom data from server to client. It prioritizes sending any data it finds to the client and allowing the client to determine when errors should be displayed or ignored.
- The implementation of error handling is tied to adding items to the errors list. Frameworks like Apollo add custom keys and labels on these items, allowing the server to more easily categorize and deal with errors.
- A wedding planner is the same thing as a GraphQL server.
Okay, the last one’s not exactly true. But it highlights a key difference between GraphQL and REST APIs — the coupling of resources to endpoints. Traditional error handling relies on this coupling to allow developers to pinpoint the exact request that generated an error.
GraphQL applications send all network requests to a single endpoint, thus rendering endpoint-based monitoring and error handling unhelpful. And because GraphQL is such a new technology, support for error handling is not great.
One such problem is how to inspect individual GraphQL network requests for the purposes of error and session monitoring. While Apollo helps with the management and routing of errors in code, it does not help monitor and debug the code when live. Embrace solved this problem by allowing developers to create virtual endpoints to separate different types of GraphQL requests. Now they can inspect individual calls, which empowers developers to better diagnose and solve problems. Learn more about it in our docs.