graphql-python/gql

Shopify error: Prefix "query" when sending a mutation

Closed this issue · 3 comments

When I have my mutation written I get an error when the request is sent that there is an unexpected sting ("query") at the start of my code. when the response is sent i can see that there is a hardcoded prefix "query" at the start of the request. I am unsure what to do now as I dont have that hard coded in my mutation so any advice on what to do would be greatly appreciated. There might be something I am doing wrong but I am just unsure. Thanks. I have transport and a client already defined in my code so not sure if there is an error in that but its not relevant to my traceback error as well which is the same as log errors.

2024-02-28 10:05:26 INFO: >>> {"query": "mutation customerRequestDataErasure($customerId: ID!) {\n customerRequestDataErasure(customerId: $customerId) {\n customerId\n userErrors {\n field\n message\n code\n }\n }\n}", "variables": {"customerId": "global_id (removed for security purposes)"}}
2024-02-28 10:05:26 INFO: <<< {"errors":[{"message":"syntax error, unexpected STRING ("query") at [1, 2]","locations":[{"line":1,"column":2}]}]}

My mutation:

    query = gql("""
            mutation customerRequestDataErasure($customerId: ID!) {
                customerRequestDataErasure(customerId: $customerId) {
                    customerId
                    userErrors {
                        field
                        message
                        code
                    }
                }
            }
            """)

    params = {"customerId": ""}

    result = client.execute(query, variable_values=params)

Sending a json with a query key is perfectly normal

  • Which backend are you using?
  • Are you sure you used a GraphQL endpoint url?

I'm using python as my backend and my url is using a graphql endpoint. it isi using shopify graphql endpoint.

Alright,

It seems the problem is that Shopify does not provide a standard GraphQL HTTP interface.

The documentation for the GraphQL API is here

And a GraphiQl interface of a demo store is available at https://shopify.dev/graphiql/admin-graphiql

Reverse engineering (with chrome dev-tools, copy as curl) what is sent on this graphiql interface shows a request looking like this:

curl 'https://shopify.dev/admin-graphql-proxy' \
  -H 'authority: shopify.dev' \
  -H 'accept: */*' \
  -H 'accept-language: en-US,en;q=0.9,la;q=0.8,fr;q=0.7,nl;q=0.6' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'cookie: shopify_experiment_assignments=%5B%5D; _shopify_dev_session=REDACTED' \
  -H 'origin: https://shopify.dev' \
  -H 'pragma: no-cache' \
  -H 'referer: https://shopify.dev/graphiql/admin-graphiql' \
  -H 'sec-ch-ua: "Not(A:Brand";v="24", "Chromium";v="122"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36' \
  -H 'x-csrf-token: REDACTED' \
  --data-raw '{"graphQLParams":{"query":"{\n  shop {\n    name\n  }\n}","variables":null},"version":"2024-01"}'

What is strange in that request is that you can see a graphQLParams key in the request instead of directly the query key. This does not seem to be documented in the Shopify GraphQL documentation.

I was able to execute a query using gql using a modified transport for Shopify which will modify the request to put it inside the GraphQLParams key.
The following code should be executable:

import logging
logging.basicConfig(level=logging.INFO)

from gql import gql, Client
from gql.transport.httpx import HTTPXTransport

class ShopifyTransport(HTTPXTransport):

    def _prepare_request(
        self,
        document,
        variable_values = None,
        operation_name = None,
        extra_args = None,
        upload_files = False,
    ):

        payload = super()._prepare_request(
            document=document,
            variable_values=variable_values,
            operation_name=operation_name,
            extra_args=extra_args,
            upload_files=upload_files,
        )

        shopify_payload = {
            "graphQLParams": payload["json"],
            "version": "2024-01",
        }

        return {"json": shopify_payload}

transport = ShopifyTransport(url="https://shopify.dev/admin-graphql-proxy")
client = Client(transport=transport)

query = gql("""
{
  shop {
    name
  }
}
""")

result = client.execute(query)

print (f"result = {result}")

You could try using that ShopifyTransport above on your own shop to see if it solves your problem.