GraphQL error handling
Riseact's GraphQL API uses two distinct mechanisms to communicate errors, depending on the context in which they occur.
Two types of error
| Type | Where it appears | When it is used |
|---|---|---|
userErrors | In the mutation payload | Validation and business logic errors |
errors (GraphQL) | At the top of the response | Structural errors, resource not found, permissions |
1. userErrors - validation errors
Mutations always return a response object that includes a userErrors field. This field is null on success, or contains a list of errors if the operation did not succeed.
Structure:
type UserError {
code: ErrorCode!
field: String # specific field that caused the error, if applicable
message: String # human-readable description
}
enum ErrorCode {
BAD_INPUT # invalid or missing input
NOT_FOUND # resource not found
NOT_UNIQUE # uniqueness violation
PROTECTED # deletion blocked by dependencies
}
Example error response:
{
"data": {
"supporterCreate": {
"supporter": null,
"userErrors": [
{
"code": "BAD_INPUT",
"field": "email",
"message": "This field is required."
}
]
}
}
}
Example success response:
{
"data": {
"supporterCreate": {
"supporter": {
"id": 42,
"email": "mario@example.com"
},
"userErrors": null
}
}
}
How to handle userErrors
Always check userErrors after each mutation before using the returned data:
const { data } = await client.mutate({ mutation: CREATE_SUPPORTER, variables });
if (data.supporterCreate.userErrors?.length) {
const errors = data.supporterCreate.userErrors;
// handle errors per field
errors.forEach(err => {
console.error(`[${err.code}] ${err.field}: ${err.message}`);
});
return;
}
const supporter = data.supporterCreate.supporter;
2. GraphQL errors - structural exceptions
Some errors are not captured in the mutation payload but propagate as standard GraphQL errors, in the errors array of the response.
Common cases:
| Situation | Error type |
|---|---|
| Resource not found in a query | GraphqlNotFound |
| Invalid or missing token | authentication error |
| Insufficient permissions | authorization error |
| Malformed query | parsing error |
Example response:
{
"data": null,
"errors": [
{
"message": "Resource not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["donation"]
}
]
}
note
The HTTP status code is almost always 200 even when GraphQL errors are present. Always check the errors field in the response, not just the HTTP status.
How to handle GraphQL errors
const response = await client.query({ query: GET_DONATION, variables: { id: 999 } });
if (response.errors?.length) {
const message = response.errors[0].message;
// e.g. "Resource not found"
console.error('GraphQL error:', message);
return;
}
Summary of the recommended pattern
async function safeMutate(client, mutation, variables) {
let response;
try {
response = await client.mutate({ mutation, variables });
} catch (networkError) {
// Network error or HTTP 5xx
throw new Error('Network error: ' + networkError.message);
}
// Structural GraphQL errors
if (response.errors?.length) {
throw new Error('GraphQL error: ' + response.errors[0].message);
}
// Business logic errors in the mutation
const rootKey = Object.keys(response.data)[0];
const result = response.data[rootKey];
if (result.userErrors?.length) {
throw new Error('Validation error: ' + result.userErrors[0].message);
}
return result;
}