Handling and documenting GraphQL errors using Apollo-Prophecy
Tags: graphql, errors, javascript, node
Published: 2018-07-17 08:32:08

---
title: Handling and documenting GraphQL errors using Apollo-Prophecy
published: true
description: 
tags: graphql, errors, javascript, nodejs
canonical_url: https://hackernoon.com/handling-and-documenting-graphql-errors-using-apollo-prophecy-apollo-7a32de3547a1
cover_image: https://cdn-images-1.medium.com/max/2000/1*W-rI1KV22Ghve32IjoClOw.png
---
*You shall fail… successfully*

*Checkout-out the [Original Medium Post](https://hackernoon.com/handling-and-documenting-graphql-errors-using-apollo-prophecy-apollo-7a32de3547a1)*

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 **well-formatted** and **easily identifiable errors** that the Client can **seamlessly read, process** and **handle** in order to **fail successfully**.

![**E.g. a successful failure**](https://cdn-images-1.medium.com/max/2000/1*H4fjHIsJZblHY6YgiHMoTg.jpeg)

GraphQL powered APIs are no **Exceptions** (pun intentional 😏) to this rule. Here is what the latest draft *(Sun, Jun 10, 2018)* of the GraphQL specification says about how error outputs should be formatted.
> 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 `message` with a string description of the error intended for the developer as a guide to understand and correct the error.
> GraphQL services may provide an additional entry to errors with key `extensions`. 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.

With this in mind, a typical error object should look something like this:
```json
    ...
    "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 “**well-formatted** and **easily identifiable**” meaning it should contain at least one field than can be seamlessly processed by a computer.

First candidate to consider is `message`, a *“string description of the error intended for the developer[…]”.* Since it is formatted to be read by a human, it could potentially be an expressive long string containing unwanted characters *(%, ç, à, $, €, @, white spaces, etc…)* thus not ideal.

According to the specification, `extensions` should be the dedicated space for any additional entry to `errors`. Here, it gives us the ability to attach a code key, providing a **machine-readable** datum that can be “**seamlessly read, processed** and **handled**”.
```ts
    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 **spec-compliant** and **identifiable**errors — thanks to `extensions` — within our resolvers.

* 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**](https://github.com/theGlenn/apollo-prophecy).

## The way of the pagan

First, let’s illustrate what maintaining errors can be like without [**Apollo-Prophecy**](https://github.com/theGlenn/apollo-prophecy). To that end we’ll be using **Apollo Server**, a prominent, spec-compliant, fully-featured and well-maintained GraphQL server implementation for nodeJS.

Because we’re using [Apollo Server](https://github.com/apollographql/apollo-server), we can use the constructor `ApolloError(message, code)`: errors thrown using this constructor produce a spec-compliant JSON output like the one above.
```ts
    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:

{% gist https://gist.github.com/theGlenn/e4f88b0fd73fbc7f0e590688d5b0827f %}

And properly handle errors like this:

{% gist https://gist.github.com/theGlenn/4b70c225f58c83163f407ea0fbfb7b74 %}

Done, right?

No, we can do better. With this configuration, we end up doing the same work twice: since **for every existing error entry** on the server we would have to write a **corresponding key** client side.

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 ***API should be self-documenting.*** 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:
> # What if errors were part of the schema?

Here is how we could exploit this:

**1.** We include errors in the schema:
```graphql
    type ErrorExtensions {
      code: String!
    }

    type Error {
      name: String!
      message: String
      extensions: ErrorExtensions
    }

    type Query {
      ...
      errors: [Error!]!
      ...
    }
```

**2.** We create the corresponding resolver on the Query field:
```ts
    ...
    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:

1. Every time our app launches, the client could **perform a query to fetch all errors codes and store them locally**. 😒 *Meh…*

1. Handle it on the dev-side by fetching and **storing errors statically in the codebase** as part of the build process. 💁 *Why not?*

Since correct error-handling is critical to the good functioning of your application, going with **option 1** would make fetching all errors’ definitions a mandatory step of the app launch process — therefore increasing loading duration.

That’s why for cleanliness and overall performance, I like the **second option** better.

## The prophet way? 🧙🏼‍

I’ve started working on [Apollo Prophecy](https://github.com/theGlenn/apollo-prophecy): a code generation Command Line interface that does what we need (and a tiny bit more!). It will:

* 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.
```sh
    [npm | yarn] install -g apollo-prophecy
```

### To generate errors like a greek God 🔮

The `generate` command will create a file containing throwable error classes. It takes as input a JSON file formatted like this:

{% gist https://gist.github.com/theGlenn/7bf151ff0e9bed0acf3a2fb102d67f2c %}

It can be run like below (if nothing is specified it will look for an **errors.json** file inside the running folder):
```sh
    apollo-prophecy generate errorsDef.json
```

Using the above **errosDef.json** the CLI will generate the following file.

{% gist https://gist.github.com/theGlenn/8fd8d7550ca0815b5976af7fa8ec007e %}

Here are the generated file key components:

* `errorsList` — plain JSON array meant to be used as documentation output. It contains all error representations with their static data: `name`, `message`, `extensions -> code`. *Always generated but empty if there is no error to generate.*

* `errorType` — GraphQL object type that we can include in our **schema definition**. It should be used alongside `errorsList` for documentation. *Always generated as is*.

* `PropheticError` — class extending ApolloError meant to be **inherited** by other errors in this file. *Always generated as is*.

* `NotAProphetError` `ProphetNotFoundWithId` — those are the two custom error classes generated with the information of the JSON file input.

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:
```ts
    import { errorsList, NotAProphetError } from './gen/GeneratedErrors'

    Query: {
      errors: () => errorsList
      getAllUsers: () => {...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 [Apollo-Prophecy](https://github.com/theGlenn/apollo-prophecy), we can do something similar with the command `ask`:
```sh
    apollo-prophecy ask [http://localhost:3000/graphql](http://localhost:3000/graphql) [--field]
```

This will send a request to the specified endpoint and try to perform a GraphQL query on the `--field` option to try and fetch errors’ information (if nothing is specified, an *“errors” field* will be queried by default).

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](https://github.com/theGlenn/apollo-prophecy)!

{% gist https://gist.github.com/theGlenn/27736df1e2a54774b8dff435bde3df35 %}

* `PropheticErrorCode` —an enum with the codes of all errors exposed in the schema.

* `errorHere` and `isThis` are the real two helper methods that enable us to handle client-side errors in a clean and reusable way.

### - errorHere(error)

When called, it returns an object that has **one property named after each errors** found on the server. Depending on the supplied argument, the called property returns either **true or false**:
```ts
    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 **one handler function named after each errors** found on the server.
```ts
    import { isThis } from `./_generated/Errors.ts`;

    ...(error) => {
      isThis(error)
      .UserNotFoundError(() => ...)
      .NotAProphetError(() => ...)
      .handle()
    }
```

Handlers return the same instance object than `isThis`, so that each function call can be chained. Once the `handle` method is called, it initiates the check and calls the corresponding handler if there is a match.

And… voilà! Thanks to the `ask` command we can keep our client-side error repository in sync with the API through the schema. By using `errorHere` and `isThis` we now have a clean and expressive way of handling errors — and look, the code is pretty too!

## Conclusion

Just like any young technology, [GraphQL](https://graphql.org/) still has gaps to fill. [Apollo-Prophecy](https://github.com/theGlenn/apollo-prophecy) is built to fill just one of these gaps: *how we implement error handling and documentation*. But this isn’t the end of the conversation; [Apollo-Prophecy](https://github.com/theGlenn/apollo-prophecy) is open-source, and I’m sure together we can come up with even better ways to improve it.

Already there is a lot of work and fixes to be done on [**Apollo-Prophecy**](https://github.com/theGlenn/apollo-prophecy); contributions and suggestions are both welcomed and needed. Please visit [Github](https://github.com/theGlenn/apollo-prophecy/issues/new) and take look at existing [issues](https://github.com/theGlenn/apollo-prophecy/issues) or even [create new one](https://github.com/theGlenn/apollo-prophecy/issues/new)s.

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 🙂.