CORS error when trying to extract product info using graphql API

Topic summary

A developer encountered a CORS (Cross-Origin Resource Sharing) error when attempting to fetch product information from Shopify’s GraphQL Admin API for a chatbot project.

The Problem:

  • Error message indicated the API response lacked the required ‘Access-Control-Allow-Origin’ header
  • The chatbot code was embedded in a Liquid template section on the store’s main page
  • Requests were being made from the storefront domain (yurtrock.com) to the admin API endpoint

Key Technical Details:

  • The script attempted to query Shopify’s Admin API at /admin/api/2024-01/graphql.json
  • Browser blocked the request due to CORS policy violations during the preflight check
  • Code included authentication headers (‘X-Shopify-Access-Token’)

Resolution:
The original poster confirmed they resolved the issue, though the specific solution was not shared in the thread. Another user requested details about the fix, but no response was provided yet.

Status: Resolved for the original poster, but the solution remains undocumented in this discussion.

Summarized with AI on November 11. AI used: claude-sonnet-4-5-20250929.

Hi Guys,

I’m trying to build a simple chatbot for a Shopify store I’m employed at. I’m a novice dev, and am trying to DIY it.

Below is the script I’m working on. I currently have all the code housed in a liquid template section on the shop’s main page (could this be what’s causing the issue).

When I type a message in the user input, the following exception gets thrown:

“An error occurred while fetching product information.”

and the console error reads:

“Access to fetch at ‘https://yurt-rock.myshopify.com/admin/api/2024-01/graphql.json’ from origin ‘https://yurtrock.com’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.”

Any help to resolve this issue would be much appreciated. Thanks!

Current script:

html
Copy code
<div id="chatbot-container" class="chatbot-container">
    <h1 class="chatbot-heading">Instructo</h1>
    <div id="chatbot-messages" class="chatbot-messages">
        <!-- Example message structure -->
        <p>Instructo: Enter the name of the product you purchased, below, to get started. Please be sure to enter the full name (Ex: Eric Harland - Drums Vol 2).</p>
    </div>
    <div class="field">
        <input id="user-input" class="search__input field__input" value="" placeholder="Type Your Message Here">
        <label class="field__label" for="user-input">Message Instructo..</label>
        <button id="send-button" class="search__button field__button">
            <svg viewBox="0 0 14 10" fill="none" aria-hidden="true" focusable="false" role="presentation" class="icon icon-arrow" xmlns="http://www.w3.org/2000/svg">
                <path fill-rule="evenodd" clip-rule="evenodd" d="M8.537.808a.5.5 0 01.817-.162l4 4a.5.5 0 010 .708l-4 4a.5.5 0 11-.708-.708L11.793 5.5H1a.5.5 0 010-1h10.793L8.646 1.354a.5.5 0 01-.109-.546z" fill="currentColor"></path>
            </svg>
        </button>
    </div>
</div>

<script>
    document.addEventListener("DOMContentLoaded", function() {
        const chatbotMessages = document.getElementById("chatbot-messages");
        const userInput = document.getElementById("user-input");
        const sendButton = document.getElementById("send-button");

        let productDescription = '';
        let customerDescription = '';

        // Default prompt message
        const defaultPrompt = "Instructo: Enter the name of the product you purchased, below, to get started. Please be sure to enter the full name (Ex: Eric Harland - Drums Vol 2).";

        // Function to add a message to the chatbot interface
        function addMessage(text, isUser) {
            const messageElement = document.createElement("p");
            messageElement.textContent = `${isUser ? 'You:' : 'Instructo:'} ${text}`;
            chatbotMessages.appendChild(messageElement);
        }

        // Function to fetch product information from Shopify database using GraphQL
        async function fetchProductInformation(productName) {
            const storeName = 'store-name'; // Replace 'your_store_name' with your actual store name
            const accessToken = 'token id'; // Replace 'your_access_token' with your actual access token

            // Construct the GraphQL query
            const graphqlQuery = `
              query {
                products(first: 1, query: "title:${productName}") {
                  edges {
                    node {
                      id
                      title
                      descriptionHtml
                    }
                  }
                }
              }
            `;

            try {
                // Send a POST request to the GraphQL API endpoint
                const response = await fetch(`https://${storeName}.myshopify.com/admin/api/2024-01/graphql.json`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-Shopify-Access-Token': accessToken
                    },
                    body: JSON.stringify({ query: graphqlQuery })
                });

                // Parse the JSON response
                const responseData = await response.json();

                // Extract product data from the response
                const productData = responseData.data.products.edges;

                // Check if any products were found
                if (productData.length > 0) {
                    const product = productData[0].node;
                    const productTitle = product.title;
                    const productDescription = product.descriptionHtml;
                    // Handle the product data as needed
                    addMessage("Product found. Title: " + productTitle + ". Description: " + productDescription, false);
                } else {
                    addMessage("Product not found.", false);
                }
            } catch (error) {
                console.error('Error fetching product information:', error);
                addMessage("An error occurred while fetching product information.", false);
            }
        }

        // Function to handle user input and generate chatbot response
        async function handleUserInput() {
            const userMessage = userInput.value.trim();
            if (userMessage !== '') {
                addMessage(userMessage, true);
                userInput.value = '';

                if (!productDescription) {
                    await fetchProductInformation(userMessage);
                } else {
                    customerDescription = userMessage;
                    addMessage("Creating your instructions...");
                    // Call the function to get ChatGPT response here if needed
                }
            }
        }

        // Function to pass product and customer descriptions to ChatGPT API and get response
        function getChatGPTResponse(productDescription, customerDescription) {
            // Implement ChatGPT API logic here if needed
        }

        // Function to clear the chat and start over
        function clearChat() {
            chatbotMessages.textContent = defaultPrompt;
            productDescription = '';
            customerDescription = '';
            userInput.value = '';
        }

        // Event listener for send button click
        sendButton.addEventListener("click", async function() {
            const userMessage = userInput.value.trim();
            if (userMessage.toLowerCase() === "done") {
                clearChat();
            } else {
                await handleUserInput();
            }
        });

        // Event listener for Enter key press
        userInput.addEventListener("keypress", async function(event) {
            if (event.key === "Enter") {
                const userMessage = userInput.value.trim();
                if (userMessage.toLowerCase() === "done") {
                    clearChat();
                } else {
                    await handleUserInput();
                }
            }
        });
    });
</script>

I have figured it out. The above message can be disregarded.

Great to hear you figured this out Gustavo!

What was the solution??

1 Like