GraphQL Apollo Server errors and the request context

Philipp Schmiedel
4 min readFeb 10, 2019

--

In my last blog post Monitor your GraphQL Apollo Server in Google Cloud I was writing about how to differentiate between the different GraphQL Apollo 2 Server error types and how to make use of the error logging and monitoring tools Google Cloud provides to you out of the box. It has shown in real-world scenarios that this introduced error logging is very useful, especially in case of unexpected errors in the business logic. In other cases however, some important information is missing in our logs…

We’re missing the context

Let’s have a look at this real world error log:

So what we can see is, that the Apollo Server throw a SyntaxError exception because a request we were receiving from our frontend application was invalid. It seems like the syntax error was in line 2 column 22 of the GraphQL query we received. Tom from the frontend team asks you: “hey.. it’s really hard for us to find the bug. Can you tell us the malformed query so we know where to search?”. And we respond: “Sorry Tom, we have no clue about the request that caused the exception”. Sad story.

Introducing GraphQL Extensions

Luckily since September 2018 (or Apollo Server 2.0.8), the request context is passed along the GraphQL Extension methods. This is not directly solving the issue that we do not have access to the context in the formatError method, however it still introduces a way to solve it. We can hook into the willSendResponse event and log the matching request in case an error has happened. This solution however was not really satisfying, because:

  • The logic of how to deal with errors was now distributed. One part was in the GraphQL Extension and another still in the formatError callback.
  • GraphQL Extensions are not documented very well, therefore looking it up over and over again for different projects was annoying over time.

As a result, the idea was born to create a GraphQL extension package that deals with the reoccurring tasks I was dealing with when handling GraphQL errors in different projects.

graphql-error-tracking-extension

The purpose of this package is to:

  • Log the GraphQL query and other request information in case of an error
  • Define error types the client should not see any details and just a generic Internal Server Error instead
  • Call a callback function if an error was hidden (which is probably the case you hit an unexpected bug) so you can forward it to another monitoring system, e.g. Google Cloud Error Reporting as shown in the last blog post

Here an example how an Apollo Server 2 configuration using the extension looks like:

To activate the extension, it is added to the extensions property of ApolloServer (line 9) and the request is added to the context property, so the extension can access it (line 20). Instead of using formatError to define which errors the client should see, we use the revealErrorTypes configuration of the extension.

Let’s see if our error log is more useful now to help the frontend team to find the bug:

  • The traceID in the logs f85423 helps us to find logs that belong to the same request
  • we can see the original request that was send to our server, in this case a closing bracket sneaked into the query
  • the HTTP headers we defined in maskHeaders are replaced by *** and not revealed to our logs

As SyntaxError is defined as an error type we forward to our clients, the response on the client side looks as follows:

Response to client with revealed error

But how does it look like if e.g. an ApolloError or GraphQLError happens which we haven’t defined in revealErrorTypes:

Response to client with unrevealed error

All the client is seeing is an Internal Server Error. However, in the background we have the real error in our logs and additionally, as defined in our onUnrevealedError callback, the stack trace was additionally reported to Google Error Reporting.

Contribute

The code was released under MIT license and can be found at npmjs or philsch/graphql-error-tracking-extension. If you have any feedback / suggestions / improvements please contribute on Github.

--

--