Skip to content

Instantly share code, notes, and snippets.

@felippenardi
Forked from swalkinshaw/tutorial.md
Last active July 25, 2018 17:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felippenardi/3348074358ec9581adf985a838066a70 to your computer and use it in GitHub Desktop.
Save felippenardi/3348074358ec9581adf985a838066a70 to your computer and use it in GitHub Desktop.
GraphQL API Design Guide

Guide: GraphQL API Design

This guide was created by VTEX for internal purposes inspired by Shopify's GraphQL API Tutorial.

Error Handling

There is no official GraphQL guidelines for handling errors, so we are stabilishing our own.

If you are coming from RESTful APIs, the following guidelines will feel counterintuitive because GraphQL is fundamentally different from RESTful APIs.

Two types of errors

First thing you have to bear in mind is that there are two different types of errors:

  • System errors (server error, network failure, incorrect query on the client-side, an so on)
  • User errors (invalid password or any other kind of form validation, wrong permissions to perform actions and so on)

This division is due to the fact that GraphQL server layer may interact not only with one API, but with several APIs in a single resolver endpoint. If one of the APIs fails but the resolver got positive response from others, it may decide to deliver both successful and failure information simultaneously.

When dealing with a Mutation, you will access these two distincts type of errors differently:

<Mutation query={...}>
  ({ data, error, loading } => ( // `error` contains the Server Errors and are not meant to be displayed to the user
     { JSON.stringify(data.userErrors) } // `data.userErrors` contains a list of errors that are meant to be displayed to the user
  ))
</Mutation>

The error within the Mutation render props are automatically filled when something throw at the resolver level.

The data.userErrors are manually field on the resolver level. This is what the schema for Mutations with userErrors looks like:

type CollectionCreatePayload {
  userErrors: [UserError]!
  collection: Collection
}

type UserError {
  message: String!
  
  # The i18n translation code
  code: String!

  # Path to input field (or other target) which caused the error.
  # i.e.: ['accounts','0','name'] indicates an error at `accounts[0].name`
  path: [String!]
}

Here, a successful mutation would return an empty list for userErrors and would return the newly-created collection for the collection field. An unsuccessful one would return one or more UserError objects, and null for the collection.

Important: Mutations should provide user/business-level errors via a userErrors field on the mutation payload. The top-level query errors entry is reserved for client and server-level errors.

In many implementations, much of this structure is provided automatically and all you will have to define is the collection return field.

For the update mutation, we follow exactly the same pattern:

type CollectionUpdatePayload {
  userErrors: [UserError]!
  collection: Collection
}

It's worth noting that collection is still nullable even here, since if the provided ID doesn't represent a valid collection there is no collection to return.

Important: Most payload fields for a mutation should be nullable, unless there is really a value to return in every possible error case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment