GraphQL errors
Handle data, errors, HTTP status codes, and GraphQLResponseError in Storefront API.
Last updated:
A GraphQL response uses a JSON envelope with data and, when execution finds problems, errors. Inspect both in addition to the HTTP status.
HTTP 200 does not guarantee an error-free execution
A GraphQL operation can return HTTP 200 and include errors. Checking only response.ok is not sufficient.
Response shape
A successful execution contains data:
{
"data": {
"store": {
"id": "7204558912004325301",
"name": "My store"
}
}
}When a resolver rejects the operation, the response includes errors. data can be null, as in this invalid argument example:
{
"errors": [
{
"message": "Product ID is invalid.",
"path": ["relatedProducts"],
"extensions": {
"code": "BAD_USER_INPUT"
}
}
],
"data": null
}| Field | Use |
|---|---|
data | Result of the requested fields when available. |
errors | List of errors found during execution. |
errors[].message | Diagnostic message. Do not use it as a stable identifier. |
errors[].path | Path to the field that caused the error, when available. |
errors[].extensions.code | Code for distinguishing known cases, when available. |
Document parsing and validation errors can use other codes from the GraphQL runtime.
Public resolver codes
Public Storefront resolvers use these values in extensions.code:
| Code | Meaning | First action |
|---|---|---|
BAD_USER_INPUT | An argument, filter, limit, or ID is invalid. | Fix the input; do not repeat the same operation unchanged. |
NOT_FOUND | The requested resource does not exist or is not visible. | Check the ID or slug, resolved store, and resource visibility. |
STORE_CONTEXT_REQUIRED | The store could not be resolved from x-Store-Domain. | Check that the header contains a configured slug or domain. |
Make decisions from extensions.code, not by comparing the text in message.
Handle errors with fetch
Check the transport status first and then the GraphQL envelope:
const response = await fetch("https://storefront.ecomiq.pe/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-Store-Domain": "my-store",
},
body: JSON.stringify({
query: "query Store { store { id name } }",
}),
});
const result = await response.json();
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
if (result.errors?.length) {
const firstError = result.errors[0];
const code = firstError.extensions?.code;
switch (code) {
case "BAD_USER_INPUT":
console.error("Fix the query arguments.");
break;
case "NOT_FOUND":
console.error("The resource does not exist or is not visible.");
break;
case "STORE_CONTEXT_REQUIRED":
console.error("Check x-Store-Domain.");
break;
default:
console.error(firstError.message);
}
throw new Error(firstError.message);
}
console.log(result.data.store);Do not access result.data before checking errors.
Handle GraphQLResponseError
@ecomiq/storefront validates data with the Zod schema passed to graphql(). The method throws GraphQLResponseError if the response contains errors or if data does not match that schema.
import {
createClient,
GraphQLResponseError,
z,
} from "@ecomiq/storefront";
const SellerData = z.object({
seller: z.object({
id: z.string(),
name: z.string(),
slug: z.string(),
}),
});
const api = createClient();
try {
const data = await api.graphql(
`
query Seller($slug: String!) {
seller(slug: $slug) { id name slug }
}
`,
SellerData,
{ slug: "acme" },
);
console.log(data.seller);
} catch (error) {
if (!(error instanceof GraphQLResponseError)) {
throw error;
}
for (const graphQLError of error.errors) {
console.error(
graphQLError.extensions?.code ?? "GRAPHQL_ERROR",
graphQLError.message,
);
}
}GraphQLResponseError.errors preserves the errors from the envelope. GraphQLResponseError.data preserves any received data; check that it is present before using it.