You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enhance documentation on error handling in GraphQL
Added section on modeling errors as data in GraphQL APIs, detailing recoverable and unrecoverable errors, and how to structure mutations and queries to handle errors effectively.
Copy file name to clipboardExpand all lines: src/pages/learn/response.mdx
+162Lines changed: 162 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -102,6 +102,168 @@ The mutation above attempts to delete two starships in a single operation, but n
102
102
103
103
As with network calls to any type of API, network errors that are not specific to GraphQL may happen at any point during a request. These kinds of errors will block communication between the client and server before the request is complete, such as an SSL error or a connection timeout. Depending on the GraphQL server and client libraries that you choose, there may be features built into them that support special network error handling such as retries for failed operations.
104
104
105
+
### Modelling Errors as Data
106
+
107
+
When returning errors to API clients, it’s important that we enable clients to create recoverable scenarios for users. This can be enabled by providing the required context to clients, along with making it easy for clients to consume those errors.
108
+
When modelling API errors for both queries and mutations we can follow two guiding principles:
109
+
110
+
-**Unrecoverable errors, returned in the errors array**
111
+
These are errors that are not the users fault (developer errors), which are generally things that the user can’t recover from. For example, the user not being authenticated or a resource not being found. This will also include scenarios such as
112
+
server crashes, unhandled exceptions and exhausted resources (for example, memory or CPU)
113
+
114
+
-**Recoverable errors (user errors), returned as data (typed errors)**
115
+
These are errors that the user can recover from or where we need to communicate something to the user. For example, input validation error or the user having hit a limit on their plan for the requested operation. This approach allows us to
116
+
utilise typed errors to provide context to clients, while allowing us to enforce a clear distinction between developer and user-facing errors. This ensures that only useful errors are returned as types and everything else developer facing will be
117
+
returned in the errors array.
118
+
119
+
#### Mutations
120
+
##### Modelling Errors
121
+
Every mutation defined in the schema returns a Payload union - this allows mutations to return their success state, along with any user facing errors that may occur. This should follow the format of the mutation name, suffixed with Payload - e.g `{MutationName}Payload`.
122
+
123
+
```graphql
124
+
unionCreateHeroPayload = CreateHeroSuccess | ...
125
+
```
126
+
127
+
This approach allows clients to query for the specific error types that they want to handle, as well as allow us to build a continuously evolving schema. When adding support for a new error, it should be added to the payload definition.
If a client does not need to consume the specific error types, they can simply rely on the `MutationError` interface:
189
+
190
+
```graphql
191
+
mutationCreateHero {
192
+
createHero {
193
+
...onCreateHeroSuccess {
194
+
// handlefields
195
+
}
196
+
...onMutationError {
197
+
message
198
+
}
199
+
}
200
+
}
201
+
```
202
+
203
+
#####Future proofing error responses
204
+
205
+
When mutations are first modelled in the schema it might be the case that there is not a need for any specific typed error to be defined. In future, you may add error types to the payload for a mutation, but it means that any existing clients utilising the mutation will need to update their code to consume any new errors. If you need to handle this scenario, a common mutation error type can be provided. For example, this `VoidMutationError` type will be included as a type of every mutation payload that do not include any other error types. This can then be removed in future when any user-facing error types are implemented for the payload.
WhiletheAPIwillnever (and should never) explicitlyreturnthis `VoidMutationError` type, itmeansthatwhenanytypeofMutationErrorisreturnedinfuture, clientswillautomaticallyreceivenewerrorswithoutneedingtoshipanychanges.
216
+
217
+
```graphql
218
+
... onMutationError {
219
+
message
220
+
}
221
+
```
222
+
223
+
To benefit from this approach, client queries will need to include the resolution of the `MutationError`.
224
+
225
+
##### Returning non-recoverable errors
226
+
227
+
In cases where non-recoverable errors need to be returned in the errors array, our error resolver will utilise the GraphQLError class. This allows us to provide an additional code to provide more context to clients where needed. Unless we need other metadata in future, the extensions should not provide any other data outside of code that needs to be portrayed to the user - if data regarding the error is required to portray information to the user, please use a typed error.
228
+
To enforce standards here, its good practice to define an `ErrorCode` enum which can then be provided to a function which will throw the error. This function allows you to centralise error logic and ensure that the backend is returning the correct error format for clients. Without this enforcement, it can be easy for the backend to become riddled with error codes.
However, therewillbeasmallamountofcaseswherethereareuser-recoverableerrorsthatmayneedtobereturnedfromqueries. Inthesecases, weshouldtreatthemthesameasmutationsandprovideanunionpayloadsothatuser-recoverableerrorscanbereturnedtotheclient.
0 commit comments