GraphQL query works through webinterface but not through code

I’m having issues creating productOptions. (Creating options and variants).
It doesn’t matter if I’m using productCreate with productOptions set, or if I’m using productOptionsCreate on an existing product. I cannot understand why and would love to have your help.

I have this query:

Mutation:

mutation productOptionsCreate($productId: ID!, $options: [OptionCreateInput!]!, $variantStrategy: ProductOptionCreateVariantStrategy) {
  productOptionsCreate(
    productId: $productId
    options: $options
    variantStrategy: $variantStrategy
  ) {
    userErrors {
      field
      message
      code
    }
    product {
      id
      variants(first: 10) {
        nodes {
          id
          title
          selectedOptions {
            name
            value
          }
        }
      }
      options {
        id
        name
        values
        position
        optionValues {
          id
          name
          hasVariants
        }
      }
    }
  }
}

Variables:

{
    "productId": "gid://shopify/Product/7081223454853",
    "options": [
      {
        "name": "Weight",
        "values": [
          {
            "name": "Any weight"
          }
        ]
      }
    ],
    "variantStrategy": "CREATE"
  }

Calling this through the webinterface yields:

{
  "data": {
    "productOptionsCreate": {
      "userErrors": [],
      "product": {
        "id": "gid://shopify/Product/7081223454853",
        "variants": {
          "nodes": [
            {
              "id": "gid://shopify/ProductVariant/41001544286341",
              "title": "Any weight",
              "selectedOptions": [
                {
                  "name": "Weight",
                  "value": "Any weight"
                }
              ]
            }
          ]
        },
        "options": [
          {
            "id": "gid://shopify/ProductOption/9066185457797",
            "name": "Weight",
            "values": [
              "Any weight"
            ],
            "position": 1,
            "optionValues": [
              {
                "id": "gid://shopify/ProductOptionValue/1755303772293",
                "name": "Any weight",
                "hasVariants": true
              }
            ]
          }
        ]
      }
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 22,
      "actualQueryCost": 16,
      "throttleStatus": {
        "maximumAvailable": 2000,
        "currentlyAvailable": 1984,
        "restoreRate": 100
      }
    }
  }
}

While calling it with this code:

async function createProductOptions(productId, options, variants) {
    console.log(`options: ${options}`);
    const mutation = `
        mutation productOptionsCreate($productId: ID!, $options: [OptionCreateInput!]!, $variantStrategy: ProductOptionCreateVariantStrategy) {
            productOptionsCreate(productId: $productId, options: $options, variantStrategy: $variantStrategy) {
                userErrors {
                    field
                    message
                    code
                }
                product {
                    id
                    variants(first: 10) {
                        nodes {
                            id
                            title
                            selectedOptions {
                                name
                                value
                            }
                        }
                    }
                    options {
                        id
                        name
                        values
                        position
                        optionValues {
                            id
                            name
                            hasVariants
                        }
                    }
                }
            }
        }
    `;

    const variables = {
        productId,
        options,
        variantStrategy: "CREATE"
    };

    const formattedMutation = mutation.trim().replace(/\s{2,}/g, ' ');

    console.log("Creating options with query:", JSON.stringify({
        query: formattedMutation,
        variables: variables,
    }, null, 2));


    try {
        const response = await fetch('shopify:admin/api/graphql.json', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ query: mutation, variables }),
        });

        const jsonResponse = await response.json();

        if (jsonResponse.errors) {
            throw new Error('GraphQL errors: ' + jsonResponse.errors.map(error => error.message).join(', '));
        }
        if (jsonResponse.data.productOptionsCreate.userErrors?.length > 0) {
            throw new Error('User errors: ' + jsonResponse.data.productOptionsCreate.userErrors.map(error => error.message).join(', '));
        }

        console.log("Created options for product ID:", productId);
        return jsonResponse.data.productOptionsCreate.product;
    } catch (error) {
        console.error("Error creating product options:", error);
        throw error;
    }
}

Yields this result:

Error creating product(s): GraphQL errors:
- `OptionCreateInput` isn't a defined input type (on `$options`)
- `ProductOptionCreateVariantStrategy` isn't a defined input type (on `$variantStrategy`)
- Field `productOptionsCreate` doesn't exist on type `Mutation`
- Variable `$productId` is declared by `productOptionsCreate` but not used
- Variable `$options` is declared by `productOptionsCreate` but not used
- Variable `$variantStrategy` is declared by `productOptionsCreate` but not used

I would really appreciate any input on this

Have you assigned your variables values? in your function i dont see any assignment operations. or you already log out the details. could your provide some details for that log?

This is what the log statement shows:

{
    "productId": "gid://shopify/Product/7081223454853",
    "options": [
      {
        "name": "Weight",
        "values": [
          {
            "name": "Any weight"
          }
        ]
      }
    ],
    "variantStrategy": "CREATE"
  }

So these are the variables that I put in. The weird thing is that when I put the same into the GraphiQL App webinterface, it works fine

Would really appreciate someones help. I’m quite stuck here now

I would start by not doing this const formattedMutation = mutation.trim().replace(/\s{2,}/g, ' '); as you don’t need to format it.

If you remove your headers, it should work. headers: { 'Content-Type': 'application/json' }, as this will be messing with the query as its not JSON its graphql

Thanks for your reply, Jordan! I appreciate it a lot. So now I tried this with the productCreate query:

async function createProduct(title, productType, vendor, options) {
  const mutation = `
      mutation productCreate($input: ProductInput!) {
        productCreate(input: $input) {
          product {
            id
            title
            productType
            vendor
            options {
              id
              name
              values
            }
            variants(first: 1) {
              edges {
                node {
                  id
                }
              }
            }
          }
          userErrors {
            field
            message
          }
        }
      }
    `;

  const variables = {
    input: {
      title,
      productType,
      vendor,
      productOptions: options
    },
  };

  console.log(JSON.stringify(variables));

  try {
    const response = await fetch('shopify:admin/api/graphql.json', {
      method: 'POST',
      body: JSON.stringify({ query: mutation, variables: variables }),
    });

    const jsonResponse = await response.json();

    if (jsonResponse.errors) {
      throw new Error('GraphQL errors: ' + jsonResponse.errors.map(error => error.message).join(', '));
    }
    if (jsonResponse.data.productCreate.userErrors?.length > 0) {
      throw new Error('User errors: ' + jsonResponse.data.productCreate.userErrors.map(error => error.message).join(', '));
    }

    return jsonResponse.data.productCreate.product;
  } catch (error) {
    console.error("Error creating product:", error);
    throw error;
  }
}

The console.log statement contains the following:

{
  "input": {
    "title": "Default Title",
    "productType": "Complete",
    "vendor": "Test",
    "productOptions": [
      {
        "name": "Weight",
        "values": [
          {
            "name": "Any weight"
          }
        ]
      }
    ]
  }
}

I still get this error:

GraphQL errors: Variable $input of type ProductInput! was provided invalid value for productOptions (Field is not defined on ProductInput)

Whereas doing the same through the graphiql webinterface does work

I would really appreciate further tips and assistance

Can you check what is being passed through as options? In your code.
I suspect that maybe an undefined value is being passed through. This would be excluded from your JSON.stringify and cause this error.

Thanks again for your reply!

I now put this in the same function:

console.log(`Options: ${options[0].values[0].name}`);
console.log(JSON.stringify(variables));

I get this output:

Options: Any weight

{
  "input": {
    "title": "Default Title",
    "productType": "Complete",
    "vendor": "Test",
    "productOptions": [
      {
        "name": "Weight",
        "values": [
          {
            "name": "Any weight"
          }
        ]
      }
    ]
  }
}

Earlier in the function I build the variables like so:

const variables = {
    input: {
      title,
      productType,
      vendor,
      productOptions: options
    },
  };

And if I put the same data that I see in the console.log statement from the variables into the Graphiql webinterface, it works no problem

Maybe it’s good to mention that I use the “Custom distribution” distribution model:

Yeah I suspect if you do console.log(Options: ${options[0].values[0].Field}); you will get undefined printed, as I think you are passing extra undefined information in the options object

Could you elaborate?

The options object doesn’t have a field Field so you would already expect options[0].values[0].Field to be undefined, no?

Printing the whole variables object shows also all that the options objects contains and it’s not more than

[
      {
        "name": "Weight",
        "values": [
          {
            "name": "Any weight"
          }
        ]
      }
]

Am I missing something here?

Can you confirm which API version you are using in both scenarios?

ProductInput.productOptions was introduced in 2024-04 (not available in 2024-01). Looking at logs, I can see several failing request appearing to match your problem trying to use 2024-01.

Hi Rob,

Thanks a lot for your reply

In my apps .toml file I have the following:

[webhooks]
api_version = "2024-10"

If I look in the app configuration on shopify.dev, I see the following:

Is there any other place I can verify what the API version is?

That section of the TOML is specific to webhooks.

I’m unclear on your implementation, but this issue sounds related where the request goes against the oldest supported API version (2024-01). This API version does not have the functionality you are looking to use.

1 Like

Thank you a lot!

So I have to include the API Version, even though I expected it to use the latest version or setting the API version elsewhere.

Instead of this:

const response = await fetch('shopify:admin/api/graphql.json', {
      method: 'POST',
      body: JSON.stringify({ query: mutation, variables: variables }),
    });

I had to put this:

const response = await fetch('shopify:admin/api/2024-10/graphql.json', {
      method: 'POST',
      body: JSON.stringify({ query: mutation, variables: variables }),
    });

And that works