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
POSTquery to the API.
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.
For server-side implementations the graphql-ruby doc on errors gives a few options.
Returning Errors Inside Resolve
You can return a
GraphQL::ExecutionError inside a
resolve function, see @027d75cf for an example.
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::RecordInvalid or catch-all
StandardError into a
GraphQL::ExecutionError using a generic rescue object, see @a0b8f58b for an example.
This is used with
resolve as follows.
Transport errors are straightforward and depend on the GraphQL client and HTTP library used. For example Graphlient’s
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
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