Errors
Overview
For developers accustomed to working with REST/HTTP APIs, transitioning to GraphQL's error handling paradigm can initially seem unfamiliar and less structured. In traditional REST/HTTP APIs, error handling typically relies on status codes returned by the server. However, GraphQL requests are consistently made using the same /graphql URL, and they consistently return a 200 OK response regardless of the outcome.
In GraphQL, errors are handled differently. Instead of signaling errors through status codes, they reside within the response payload alongside any returned data. This approach allows for more granular error messaging and facilitates a tighter integration between the requested data and encountered errors.
Error Handling in GraphQL
GraphQL boasts transport agnosticism, meaning it isn't bound to any specific transport mechanism for data transmission between client and server. This flexibility allows GraphQL APIs to leverage Web Sockets as an alternative to HTTP for client-server communication, providing adaptability tailored to various use cases. Consequently, GraphQL APIs operate independently of HTTP methods such as GET, PUT, and POST, and HTTP status codes are not integral to their functionality.
In GraphQL, error handling takes a distinct approach. Errors are seamlessly integrated into server responses. When an error arises, the server includes an errors field within the response, encapsulating relevant error information. Clients can discern the success or failure of their requests by examining this errors field, fostering a robust error-handling mechanism within GraphQL's paradigm.
In the JSON response provided, errors are encapsulated within an errors attribute, which contains an array of objects. Each object within this array comprises two main attributes:
- message: This attribute contains a descriptive message detailing the error encountered during the request.
- extensions: Within this attribute lies additional error metadata, including the error code.
By structuring errors in this manner, GraphQL facilitates clear and concise error reporting, allowing clients to easily interpret and handle errors within the response.
Example 1 - Invalid or empty authorization token
Query
query Order($orderId: ID!) {
order(id: $orderId) {
id
}
}
Variables
{
"orderId": "9f1ad188-ec00-47c7-911e-859cf5cc7bda"
}
Response
{
"errors": [
{
"message": "Invalid token",
"extensions": {
"code": "INVALID_TOKEN"
}
}
]
}
Example 2 - Bad request
Query
query Couriers($countryCode: String!) {
couriers(countryCode: $countryCode) {
id
name
}
}
Variables
// Wrong value, it expects an ISO 3166-1 alpha-2 string like "NL"
{
"countryCode": 33
}
Response
{
"errors": [
{
"message": "Variable \"$countryCode\" got invalid value 33; String cannot represent a non string value: 33",
"extensions": {
"code": "BAD_USER_INPUT"
}
}
]
}
Distinguishing Errors: Sandbox vs. Production Environments
Errors encountered in sandbox environments may differ from those in production. In a sandbox environment, errors are often more verbose and detailed, providing developers with additional information to aid in debugging and testing. This comprehensive error reporting is essential for identifying and resolving issues during development phases without compromising sensitive data.
In contrast, errors in production environments are typically more concise and sanitized to mitigate security risks. While still informative, they are designed to provide enough detail for troubleshooting while protecting sensitive information from exposure.
Understanding these distinctions is crucial for developers transitioning between environments. It ensures appropriate error handling strategies are implemented, tailored to the specific needs and security requirements of each environment. Please, develop and test your code using the error structures provided for production.
Sandbox error structure
{
"errors": [
{
"message": "Variable \"$countryCode\" got invalid value 33; String cannot represent a non string value: 33",
"locations": [
{
"line": 1,
"column": 16
}
],
"extensions": {
"code": "BAD_USER_INPUT",
"stacktrace": [
"GraphQLError: String cannot represent a non string value: 33",
" at GraphQLScalarType.parseValue (/opt/local/graphql/node_modules/graphql/type/scalars.js:209:13)",
" at coerceInputValueImpl (/opt/local/graphql/node_modules/graphql/utilities/coerceInputValue.js:151:26)",
" at coerceInputValueImpl (/opt/local/graphql/node_modules/graphql/utilities/coerceInputValue.js:49:14)",
" at coerceInputValue (/opt/local/graphql/node_modules/graphql/utilities/coerceInputValue.js:32:10)",
" at coerceVariableValues (/opt/local/graphql/node_modules/graphql/execution/values.js:132:69)",
" at getVariableValues (/opt/local/graphql/node_modules/graphql/execution/values.js:45:21)",
" at /opt/local/graphql/node_modules/@apollo/gateway/dist/index.js:526:67",
" at NoopContextManager.with (/opt/local/graphql/node_modules/@opentelemetry/api/build/src/context/NoopContextManager.js:25:19)",
" at ContextAPI.with (/opt/local/graphql/node_modules/@opentelemetry/api/build/src/api/context.js:60:46)",
" at NoopTracer.startActiveSpan (/opt/local/graphql/node_modules/@opentelemetry/api/build/src/trace/NoopTracer.js:65:31)"
]
}
}
]
}
Production error structure
{
"errors": [
{
"message": "Variable \"$countryCode\" got invalid value 33; String cannot represent a non string value: 33",
"extensions": {
"code": "BAD_USER_INPUT"
}
}
]
}