Understanding GraphQL Error Handling mechanisms in Spring-boot
Tags: java, graphql, springboot, api
Published: 2020-06-13 16:53:11

#**Overview**
This article is the second part of the series GraphQL, from Theory to real-world with Spring boot.

Today’s episode is about setting up your Spring-boot project to host your first GraphQL API entry points then call them using the web-based tool GraphiQL.

It also aims to bring an in-depth understanding of how the [Graphql Java Implementation](https://github.com/graphql-java/graphql-java) (graphql-java) deals with error handling and especially how to rely on that, by using the [GraphQL Spring Framework boot starter](https://github.com/graphql-java-kickstart/graphql-spring-boot), to handle efficiently errors within our app, as this is an aspect that is often overlooked by early graphql adopters.

#**TL;DR**

Check the corresponding video out:
{% youtube lxOXDI_dvt8 %}

Before we start, let’s introduce some context.

#**GraphQL quick summary**

GraphQL is a specification that defines not only a new query language for APIs but also how data is returned in response to these queries.
It is an alternative to using Restful APIs in the sense that it solves some of their drawbacks with new features such as these described ones:

* The client has full control over the data returned to him, he receives only what he needs, no more, no less. No unnecessary data is therefore loaded onto the network. No over-etching.

* The client can request several resources at once in a single request, no need to send multiple requests. Ideal for slow mobile connections.

* The server defines what is possible or not with the notion of types, to force the client to request only what is possible.

#**With great power comes great responsibilities**

According to the features mentioned above, the client must be highly considered when setting up such an API. 

It is more than important to return a response format that is consistent and predictable across several scenarios, to make it easier for the customer to consume the response.

According to the specification, a GraphQL response should follow this pattern:

**A GraphQL response might always have the HTTP status 200 OK.**

HTTP error codes are not relevant when using GraphQL because,
If a request fails, the JSON payload of the server’s response will contain a root field called *“errors”* that contains precise information about the problems that occurred on the server-side.

Moreover, since GraphQL allows for multiple operations to be sent in the same request, well, it’s possible that a request fails partially and returns actual data and errors.

**The response body could contain the following fields:**
**_“data”_**: where the data resulting from the operation reside.
**_“errors”_**: where all the errors are filled in.
**_“extensions”_**: for additional information about the response

**The _"errors"_ field, should contain the following fields :**
**_"message"_**: a key describing the error
**_"locations"_**: a list of graph coordinates where the errors have occurred.
**_"path"_**: a key describing the absolute path from the root of the graph to the field where an error has occurred.
**_"extensions"_** key for the metadata related to the errors.

You can learn more about GraphQL response format by referring to the [specification part that deals with this topic](https://medium.com/r/?url=https%3A%2F%2Fspec.graphql.org%2FJune2018%2F%23sec-Response).

As far as we are concerned, we will just focus on the **_"errors"_** key.

By relying on the graphql-java and graphql-spring-boot within a spring-boot application, we will dissect the underlying mechanism of errors handling within these libraries to let you know what are your possibilities when it comes to customizing some of the default error handling mechanisms.

#**Application Set up**

We will use a simple spring-boot `sample-graphql-error-handling` that will expose two endpoints:

`createUser(username:string, password:string)` that fails if a user is already registered with the same username.

`getUser(username:string,password:string)` that fails if no user is existing for the given username.

Generate the project using [Spring Initializr](https://medium.com/r/?url=https%3A%2F%2Fstart.spring.io%2F) and add the following dependencies to the pom.xml file:



{% gist https://gist.github.com/imphilippesimo/c420e455343f8507c535fec9ffb9d303 %}
<figcaption>Minimal maven dependencies to enable graphql implementation within a spring boot app</figcaption>



Our User model Class: User.java



{% gist https://gist.github.com/imphilippesimo/dfe6f385f0bff5a4d8db1ed1d0d5816a %}
<figcaption>The User model class</figcaption>



The Schema definition file: `src/main/resources/UserQL.graphqls`



{% gist https://gist.github.com/imphilippesimo/b03e37cd53925fd434f6218262ddff57 %}
<figcaption>User.graphqls</figcaption>



The schema speaks for itself, we will have a read request `getUser` which will return a `User` corresponding to the `username` and `password` given as parameters, and a write request which will register a `User` with its credentials as parameters.

We will use a service class, acting as our business logic that will maintain a list of in-memory users. This service will be injected into our resolvers later.



{% gist https://gist.github.com/imphilippesimo/2a7c6f189fab5cab87dcfbb3231352b4 %}
<figcaption>UserService.java</figcaption>


The `createUser` method will return a `UserAlreadyExistsException` if a user already exists. 

Similarly, the `getUsermethod` will return a `UserNotFoundException` in case no user is found.

Next, we have the code of our exceptions:



{% gist https://gist.github.com/imphilippesimo/5fed81235df9ef4cc79dc6187bdbefee %}
<figcaption>UserAlreadyExistsException.java</figcaption>



{% gist https://gist.github.com/imphilippesimo/e1880800f48cc7e4ad4f69e30dfaac57 %}
<figcaption>UserNotFoundException.java</figcaption>



We will rely on these customized exceptions to return relevant information to the customer.

Finally, the resolvers code:

{% gist https://gist.github.com/imphilippesimo/98422f641b0cfc210787cd67e448e35e %}
<figcaption>UserQuery.java</figcaption>


{% gist https://gist.github.com/imphilippesimo/98422f641b0cfc210787cd67e448e35e %}
<figcaption>UserMutation.java</figcaption>

Now that the project is set up, we can start sending the requests and analyze the errors. 

Run the spring boot app and launch the graphiql tool within the browser with: `http://localhost:8080/graphiql` if you are running on the local machine with the default spring boot app port.

#__The generic error (case#1)__

Within the graphiql tool, let's start by creating a user:

```graphql
mutation {
  createUser(username: "johndoe", password: "pwd") {
    id
  }
}
```
Everything is OK and the user is indeed created as confirmed by the answer:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fhc3kxbejx9e7h9zmnrr.png)

If we try to get a non-existing user:

```graphql
mutation {
  createUser(username: "johndoe", password: "def") {
    id
  }
}
```
![Alt Text](https://miro.medium.com/max/1300/0*30BZQYWDtA5MI1Yj)

Not only are the details as defined by the specification missing, but the error message is also inconsistent, as it indicates a server-side error yet it is a client-side error.

Also, if we try to create a user that is already existing:

```graphql
mutation {
  createUser(username: "johndoe", password: "pwd") {
    id
  }
}
```
![Alt Text](https://miro.medium.com/max/1300/0*30BZQYWDtA5MI1Yj)

Let’s consider what just happened as __CASE #1__

For __CASE #2__ : We need to customize the information according to the error that occurred while giving the client additional information about what happened in the backend so that he can react accordingly.

#**Customizing errors (case#2)**

We've already taken a step forward by defining and throwing problem-related customized exceptions. However, these exceptions need to be transformed into GraphQL errors that respect the standard format before being sent to the client.

`graphql-java` provides the `GraphQLError` interface representing a GraphQL standard error that our exceptions will implement.

Our exceptions become:

{% gist https://gist.github.com/imphilippesimo/78c3493ead0e6aefb090929fedf8a274 %} 
<figcaption>UserAlreadyExistsException.java as GraphQLError</figcaption>




{% gist https://gist.github.com/imphilippesimo/7348b1c3158425e39d324b372ce6d918 %} 
<figcaption>UserNotFoundException.java as GraphQLError with extra data</figcaption>



Overriding `getLocations()` and `getErrorType()` is mandatory.

We will just return `null`in these methods because they are ignored by the default error handler anyway. 
We will see why later.

In the case of wrong identification data, we want to add an *__“invalidField”__* field under the key *__“extensions”__* of the error to indicate to the customer which field is in error and needs to be corrected.

This is why we redefine the `getExtensions()` method in `UserNotFoundException` 

Don’t forget to change the instantiation of the exception in `UserService.java`.

```java
throw new UserNotFoundException(“We were unable to find a user with the provided credentials”, “username”);
```
Let’s start the application again and run the same test scenario with graphiql to compare the results.

Let’s start by creating a user, the answer is, unsurprisingly, this one:



![Alt Text](https://miro.medium.com/max/1256/0*Z2sAOf2HBKv3cHj8)



Then, if you try to retrieve a user that doesn’t exist, the request results in the following error:



![Alt Text](https://miro.medium.com/max/1400/0*DMisGK5Fd97sOMKu)



Note the invalid field present in the extensions block of the result.

Also, If we try to create a user with an existing username, the response is as follows:



![Alt Text](https://miro.medium.com/max/1400/0*gF2OoTMaQs1LiqIL)



Let’s dissect what just happened in both cases #1 and #2:

When we throw an exception while fetching data:

### 1. The exception is handled by default by the `SimpleDataFetcherExceptionHandler` 

The handler wraps 4 things: the thrown custom exception (the RuntimeException), the exception message, the error locations, and the extensions, within an ExceptionWhileDataFetchingerror (which is an implementation of the GraphQLErrorinterface).
Then, the ExceptionWhileDataFetching is added to the list of errors of the query result. As we can see in the following [SimpleDataFetcherExceptionHandler](https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/execution/SimpleDataFetcherExceptionHandler.java) code.



{% gist https://gist.github.com/imphilippesimo/966bf5e0b7ae51851aa5c4246a28cc79 %} 
<figcaption>SimpleDataFetcherExceptionHandler.java</figcaption>



At line 10, a new `ExceptionWhileDataFetching` is constructed with the error path, our exception, and the sourceLocation.

*Remember I told you the methods getLocations() and getErrorType() of our exception were ignored?
Well here’s why:*



{% gist https://gist.github.com/imphilippesimo/057eb59581de53282e5eac7983f885e7 %} 
<figcaption>ExceptionWhileDataFetching.java</figcaption>



*In this code snippet from the [ExceptionWhileDataFetching](https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/ExceptionWhileDataFetching.java) class, only the methods getMessage() and getExtensions() are called, the path and the sourceLocation are already provided upon class instantiation.*


### 2. The DefaultGraphQLErrorHandler

After the `SimpleDataFetcherExceptionHandler` process, another handler, defined by the **graphql-spring-boot** library comes into action to handle the returned list of errors.
He is the `GraphQLErrorHandler`: the default implementation is the `DefaultGraphQLErrorHandler`

These 2 steps can be illustrated as follows: 



![Alt Text](https://miro.medium.com/max/1400/1*kT6gYRkoJycrhbg-0TjuMQ.png)
<figcaption>The Simple DataFetcherExceptionHandler process</figcaption>



![Alt Text](https://miro.medium.com/max/1400/1*yea_Jb-2wYm5q-kta951Tw.png)
<figcaption>The DefaultGraphQLErrorHandler process</figcaption>



**In case#1**: Our custom exceptions **were not** instances of **GraphQLErrors**, the `DefaultGraphQLErrorHandler` considered them as internal server errors, containing **details that should not reach the client**. They were filtered and converted to `GenericGraphQLError` as illustrated.

**In case#2**: Our custom exceptions **were** instances of **GraphQLErrors**, hence they were not reduced to Generic graphQL errors.

This behavior is described in the `DefaultGraphQLErrorHandler::processErrors()` method.



{% gist https://gist.github.com/imphilippesimo/f4bfb6bbcc9e79422c2932f2cc3efbb4 %}
<figcaption>DefaultGraphQLErrorHandler::processErrors()</figcaption>



Now we clearly understand why we get such a useful error response and a valid format for **case#2**.

If the `SimpleDataFetcherExceptionHandler` behavior doesn’t fit your use case, you can create a `CustomDataFetcherExceptionHandler`implementing `DataFectcherExceptionHandler`, then define your own logic and throw something different than an `ExceptionWhileDataFetching` GraphQLError.

However, this is not something I will advise as you can find yourself breaking the rules of the GraphQL Specification by sending non-standard error messages. Unless you really understand what you are trying to implement.

Besides, customizing this behavior implies modifying the query execution strategy. As graphql-spring-boot integration is relying on graphql-java to implement the query execution strategy, you will have to deal with them, which, IMHO, does not worth it, as they already implement a pretty good behavior.

Similarly, If the `DefaultGraphQLErrorHandlerbehavior` doesn’t fit your use case, you can create a `CustomGraphQLErrorHandler` implementing `GraphQLErrorHandler`, then define your own logic and handle GraphQLErrors differently.

Let’s supposed we are not satisfied by the string: `Exception while fetching data (/xxxx)` which is automatically appended to the error message sent to the user, and we just want our original message in the error, without any fancies. We will send our unwrapped original custom exception to the final user by defining a `CustomGraphQLErrorHandler`.

#**Sending unwrapped exception**

We create the CustomGraphErrorHandler as follows:



{% gist https://gist.github.com/imphilippesimo/e5b30ae5eaeac166cfd89d1ca8e5e901 %}
<figcaption> CustomGraphQLErrorHandler.java </figcaption>


We override the `processErrors` method by telling the handler to: first check if the exception is an `ExceptionWhileDataFetching`  then extract our original exception( `UserNotFoundException`, `UserExistsException` …) and return it. Otherwise, the handler will just return the received exception as it is.

If we try to find a non-existing user, we get the following error:

![](https://miro.medium.com/max/1400/0*BfW6m4elgY0BEhDI)



As our exceptions are sent back to the user unwrapped, the message is now concise and precise.



#**Conclusion**

In this article, we were focusing two notions: 

Setting up your Spring-boot project to host your first GraphQL API entry points, 

Understanding how the graphql-java implementation and graphql-spring-boot handle errors triggered within an application. 


We have covered the following sections:

* Setting up a sample project
* Understanding the standard Graphql error response format
* Diving into the mechanisms behind generic graphql errors and more   
  tailored ones
* Adding additional custom information to the error
* Getting to know the possibilities of further error customizations.

The Github repo about this article is available [here](https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fzero-filtre%2Fspringboot-graphql-error-handling).

In the [next article](https://medium.com/@philippechampion58/secure-your-graphql-api-within-a-spring-boot-app-72961fbe9232?source=friends_link&sk=447765d64a6a7da1d4d9eec744c78e44), we will see how to Secure our APIs with pretty simple steps.

Thanks for reading, feedback is a gift 🎁.