This guide was created by VTEX for internal purposes inspired by Shopify's GraphQL API Tutorial.
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.
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.