In a previous post I walked you through building and consuming a GraphQL API in Ruby. That was the happy path. In this post we’ll generate and handle some errors.

Errors in a GraphQL API

GraphQL APIs can fail in three ways.

A client-side error, eg. a query parsing error based on well-known schema.

A server-side error, eg. a failure to create a database record behind the scenes.

A transport error, eg. a server failing to respond with a 200 status to a POST query to the API.

Client-Side Errors

Client-side errors include parser and validation errors that don’t require a roundtrip to the server. Parsing an invalid query with any client library based on graphql-ruby will raise a GraphQL::ParseError, see @d42b6238 for an example.

Server-Side Errors

Returning Errors Inside Resolve

You can return a GraphQL::ExecutionError inside a resolve function, see @027d75cf for an example.

resolve->(_object,_inputs,_ctx){GraphQL::ExecutionError.new('This has not been implemented yet.')}

The response contains both a nil value in data and an errors field with an error at the executionError path with a message.

Handling Predictable Errors

You can turn typical errors, such as ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid or catch-all StandardError into a GraphQL::ExecutionError using a generic rescue object, see @a0b8f58b for an example.

Transport Errors

Transport errors are straightforward and depend on the GraphQL client and HTTP library used. For example Graphlient’s FaradayAdapter uses Faraday::Response::RaiseError, which will turn any non-200 HTTP status code into an exception, see adapters/http/faraday_adapter.rb#24.

Client-Side Errors or Exceptions?

This is often a matter of preference or strong opinions.

I believe that any unexpected behavior outside of a happy path should raise an exception unless it can be explicitly avoided ahead of time. I also prefer to deal with two scenarios rather than three: it worked and it didn’t work vs. it worked, it didn’t work in some predictable way and it didn’t work in some unpredictable way. Practically, this means I want to see a StandardError when something failed, vs. having to check for an .errors field in my business logic.

The Github graphql-client library does not raise exceptions except when otherwise for all types of errors and parses server-side errors in potentially problematic ways (see graphql-client#132), leaving you having to both handle exceptions and check for .data.errors and .errors.

The graphlient library built on top of graphql-client attempts to make sense out of the many scenarios and always raises exceptions of Graphlient::Errors::Error variety for server-side problems. It’s simpler to use and requires less if statements.