You shall fail… successfully Errors are common to all computer programs; they might be hard to maintain, but properly dealing with them is without any doubt the most critical part of building applications. In the context of a Client/Server architecture we need the Server to output and that the Client can and in order to . well-formatted easily identifiable errors seamlessly read, process handle fail successfully E.g. a successful failure GraphQL powered APIs are no (pun intentional 😏) to this rule. Here is what the latest draft of the GraphQL specification says about how error outputs should be formatted. Exceptions (Sun, Jun 10, 2018) If the operation encountered any errors, the response map must contain an entry with key . _errors_ Every error must contain an entry with the key with a string description of the error intended for the developer as a guide to understand and correct the error. _message_ GraphQL services may provide an additional entry to errors with key . This entry, if set, must have a map as its value. This entry is reserved for implementors to add additional information to errors however they see fit, and there are no additional restrictions on its contents. _extensions_ With this in mind, a typical error object should look something like this: ..."errors": [ { "message": "Only Prophets can do this", "locations": [ ... ], "path": [ ... ], "extensions": { "code": "NOT_A_PROPHET", "timestamp": "Thu Jun 21 17:03:00 UTC 2018" } } ]... Remember that we want the error output to be “ and ” meaning it should contain at least one field than can be seamlessly processed by a computer. well-formatted easily identifiable First candidate to consider is , a Since it is formatted to be read by a human, it could potentially be an expressive long string containing unwanted characters ( etc…) thus not ideal. message “string description of the error intended for the developer[…]”. %, ç, à, $, €, @, white spaces, According to the specification, should be the dedicated space for any additional entry to Here, it gives us the ability to attach a key, providing a datum that can be extensions errors . code machine-readable “ seamlessly read, processed and handled ”. if (error.extensions.code === "NOT_A_PROPHET") { // Do Something} Moving forward 🏇 We just saw that there are guidelines on how to output errors in the context of a GraphQL API. With that we should be able to: Throw and output and errors — thanks to — within our resolvers. spec-compliant identifiable _extensions_ Identify and handle errors client-side to . fail successfully However, the specification doesn’t specify guidelines for issues like APIs errors documentation, retry or failure handling, meaning that there are countless ways to properly arrange our code base for that purpose. The absence of explicit convention led me to build . Apollo-Prophecy The way of the pagan First, let’s illustrate what maintaining errors can be like without . To that end we’ll be using , a prominent, spec-compliant, fully-featured and well-maintained GraphQL server implementation for nodeJS. Apollo-Prophecy Apollo Server Because we’re using , we can use the constructor : errors thrown using this constructor produce a spec-compliant JSON output like the one above. Apollo Server ApolloError(message, code) throw new ApolloError("Only Prophets can do this", "NOT_A_PROPHET"); In order to make it easier for us to store errors we could organize our server-side code the following way: And properly handle errors like this: Done, right? No, we can do better. With this configuration, we end up doing the same work twice: since on the server we would have to write a client side. for every existing error entry corresponding key I don’t know about you but I prefer to say DRY. To leverage API documentation 📑 One of the most interesting propositions of GraphQL is that While this is usually done through a mechanism named “introspection queries” — giving us detailed information about the fields and types in our schema — this doesn’t mean that we cannot add documentation material to the schema itself: API should be self-documenting. What if errors were part of the schema? Here is how we could exploit this: We include errors in the schema: 1. type ErrorExtensions { code: String!} type Error { name: String! message: String extensions: ErrorExtensions} type Query { We create the corresponding resolver on the Query field: 2. ...const resolvers = {Query: {...errors: { ... }}}... That’s cool but what about the client? 🤷 Assuming that information about errors are accessible through our APIs, we need to find a way to access them from the client, keeping in mind that we want to avoid doing the same work twice. From here we can discuss two different implementations: Every time our app launches, the client could . perform a query to fetch all errors codes and store them locally 😒 Meh… Handle it on the dev-side by fetching and as part of the build process. storing errors statically in the codebase 💁 Why not? Since correct error-handling is critical to the good functioning of your application, going with would make fetching all errors’ definitions a mandatory step of the app launch process — therefore increasing loading duration. option 1 That’s why for cleanliness and overall performance, I like the better. second option The prophet way? 🧙🏼 I’ve started working on : a code generation Command Line interface that does what we need (and a tiny bit more!). It will: Apollo Prophecy Generate errors that we can throw in our resolvers and expose through the schema as documentation — apollo-prophecy generate Query the server schema and generate file with methods and helpers to gracefully consume errors — apollo-prophecy ask The goal is to always keep you server and client errors repository in sync. First, install it through your favorite package manager. [npm | yarn] install -g apollo-prophecy To generate errors like a greek God 🔮 The command will create a file containing throwable error classes. It takes as input a JSON file formatted like this: generate It can be run like below (if nothing is specified it will look for an file inside the running folder): errors.json apollo-prophecy generate errorsDef.json Using the above the CLI will generate the following file. errosDef.json Here are the generated file key components: — plain JSON array meant to be used as documentation output. It contains all error representations with their static data: , , . errorsList name message extensions -> code Always generated but empty if there is no error to generate. — GraphQL object type that we can include in our . It should be used alongside for documentation. . errorType schema definition errorsList Always generated as is — class extending ApolloError meant to be by other errors in this file. . PropheticError inherited Always generated as is — those are the two custom error classes generated with the information of the JSON file input. NotAProphetError ProphetNotFoundWithId We can use all these elements in our server. Given that we need errors to be part of our schema, we can do the following: import { errorsList, NotAProphetError } from './gen/GeneratedErrors' Query: {errors: () => errorsListgetAllUsers: () => {...throw new NotAProphetError()},} Hmm ok… Does that make us prophets now? 🤔 Not yet; prophets need to communicate with gods in order to anticipate the future, don’t they? Using , we can do something similar with the command : Apollo-Prophecy ask apollo-prophecy ask http://localhost:3000/graphql [--field] This will send a request to the specified endpoint and try to perform a GraphQL query on the option to try and fetch errors’ information (if nothing is specified, an will be queried by default). --field “errors” field Below is an extremely simplified version of the generated file. If you want to have an idea of what it really looks like go and ! try it yourself —an enum with the codes of all errors exposed in the schema. PropheticErrorCode and are the real two helper methods that enable us to handle client-side errors in a clean and reusable way. errorHere isThis - errorHere(error) When called, it returns an object that has found on the server. Depending on the supplied argument, the called property returns either : one property named after each errors true or false import { errorHere } from `./_generated/Errors.ts`; ...(error) => {if(errorHere(error).isNotAProphetError){// Do something} else if(errorHere(error).isProphetNotFoundWithId){// Do something else}} - isThis(error) When called, it returns an object that has found on the server**.** one handler function named after each errors import { isThis } from `./_generated/Errors.ts`; ...(error) => {isThis(error).UserNotFoundError(() => ...).NotAProphetError(() => ...).handle()} Handlers return the same instance object than , so that each function call can be chained. Once the method is called, it initiates the check and calls the corresponding handler if there is a match. isThis handle And… voilà! Thanks to the command we can keep our client-side error repository in sync with the API through the schema. By using and we now have a clean and expressive way of handling errors — and look, the code is pretty too! ask errorHere isThis Conclusion Just like any young technology, still has gaps to fill. is built to fill just one of these gaps: . But this isn’t the end of the conversation; is open-source, and I’m sure together we can come up with even better ways to improve it. GraphQL Apollo-Prophecy how we implement error handling and documentation Apollo-Prophecy Already there is a lot of work and fixes to be done on ; contributions and suggestions are both welcomed and needed. Please visit and take look at existing or even s. Apollo-Prophecy Github issues create new one If you’ve come this far, thank you for reading ❤️ I really hope you enjoyed this post and I’d love to hear your thoughts and feedback 🙂. _apollo-prophecy - 🔮 GraphQL error management made Easy, generate custom machine-readable errors for Apollo…_github.com theGlenn/apollo-prophecy