Discuss all the new features introduced with the new product model in GraphQL.
So I am developing a custom app to run some algorythm and I need to fetch the products from my store. I've used fetch API for that, along with the GraphQL API version 2024-04. It all worked succesfully since a few days, when I suddenly started getting CORS policy errors and I'm kinda running out of time figuring out what could cause the issue. I guess it's Shopify related since it worked and I didn't change anything in the code when it stopped working.
Here's the error I am getting:
Access to fetch at 'https://corsproxy.io/?https://<MY_STORE_NAME>/admin/api/2024-04/graphql.json' from origin 'https://www.kubuk.ro' 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.
Here's an example of my function that fetches the products data:
export const gamesFinder = (async () => { const games = []; const token = MY_TOKEN_FROM_ENV; const headers = { "X-Shopify-Access-Token": token, Accept: "application/json", "Content-Type": "application/graphql", }; const url = 'https://corsproxy.io/?https://<MY_STORE_NAME>/admin/api/2024-04/graphql.json'; let after = null; let body = `{ products(first:10`; if (after) { body += `, after:"${after}"`; } body += `) { nodes { id, title, metafields(first:20) { nodes { key, value } } variants(first:20) { nodes { id } } } pageInfo { hasNextPage, endCursor } } }`; const response = await fetch(url, { method: "POST", headers, body, }); let data = await response.json(); games.push(...data.data.products.nodes); while (data.data.products.pageInfo.hasNextPage) { after = data.data.products.pageInfo.endCursor; body = `{ products(first:10`; if (after) { body += `, after:"${after}"`; } body += `) { nodes { id, title, metafields(first:20) { nodes { key, value } } variants(first:20) { nodes { id } } } pageInfo { hasNextPage, endCursor } } }`; const response = await fetch(url, { method: "POST", headers, body, }); data = await response.json(); games.push(...data.data.products.nodes); } return games; });
Note: I've tried using a different proxy, Axios to fetch, even older API versions but didn't solve anything. Fetching from Postman works just fine, I don't get any CORS error there.
Also, I've developped another feature which demanded me write code in HTML, CSS and JAVASCRIPT in the store's theme template code in order to leave some ratings on the orders. (this might not be the best workaround or practice but this is the only way I could achieve this). Now here I've used the same logic in my javascript I had a fetch function that had sent a request via the GraphQL API so I could fetch and eventually modify some metafields of orders. This used to work aswell but it seems like it stopped working because of CORS policy. I am getting the same error.
Here's the part of the code that I've used in the js:
// function to query data const fetchOrderData = ( url, token, orderId ) => { // graphql query for the order const orderInfoQuery = ` { order(id: "gid://shopify/Order/${orderId}") { id metafields(first: 10, namespace: "custom") { nodes { id namespace key value type definition{ id } } } } } `; return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': token }, body: JSON.stringify({ query: orderInfoQuery }) }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { const metafields = data.data.order.metafields.nodes; let ratingId = metafields.find(metafield => metafield.key === 'ratings'); let ratingValues = []; if (ratingId) { ratingId = ratingId.id; ratingValues = metafields.find(metafield => metafield.key === 'ratings'); if (ratingValues) { ratingValues = ratingValues.value; } } return {ratingId, ratingValues}; }) .catch(error => { console.error('Error querying order info:', error); }); }; // here is the call to that function
const apiUrl = 'https://corsproxy.io/?https://<MY_STORE_NAME>/admin/api/2024-04/graphql.json';
const accessToken = MY_ACCESS_TOKEN;
fetchOrderData(apiUrl, accessToken, orderId) .then(({ ratingId, ratingValues }) => { // metafield id and ratings should be got out if they exist ratingMetafield = ratingId; // if there is no metafield for the ratings (if the ratings metafield hasnt been set yet <=> if its value is empty on shopify admin page) create it and set a default value of [] if (!ratingMetafield) { // call the function to create this metafield createOrderMetafield(apiUrl, accessToken, orderId) .then(newMetafieldId => { // if the new metafield has been created update the metafieldID to make it possible to rate instantly without having to refresh the page ratingMetafield = newMetafieldId; }) .catch(error => { console.log(error); }); } else { ratings = JSON.parse(ratingValues); ratings.forEach((rating) => { const btnRatingIndex = Array.from(button).findIndex(btn => btn.dataset.gameId === rating.id); if (btnRatingIndex !== -1) { button[btnRatingIndex].innerText = rating.rating; } }); } // if a star is pressed get the value from the input and set it as the button's text that has been pressed to open the current star rating const starRatingForms = document.querySelectorAll('[id^="star-rating-form-"]'); starRatingForms.forEach(form => { form.addEventListener("click", function(event) { if (event.target.matches("input[type=radio][name=rating]")) { const ratingValue = event.target.value; const lineItemId = event.target.id.split("-")[0]; const ratingButton = document.getElementById(`rating-button-${lineItemId}`); const gameId = ratingButton.dataset.gameId; if (ratingButton) { // set the label for the button that opens the rating stars ratingButton.textContent = ratingValue; const ratingStars = ratingButton.parentNode.parentNode.querySelector('.star-rating-wrapper'); // when a value has been selected send a request to the backend to set the rating of the given item in the order if (orderId && gameId) { const existingRatingIndex = ratings.findIndex(rating => rating.id === gameId); if (existingRatingIndex !== -1) { // Update the rating value if the element exists ratings[existingRatingIndex].rating = Number(ratingValue); } else { // Add a new element if the element doesn't exist ratings.push({ id: gameId, rating: Number(ratingValue) }); } updateOrderRatings(ratingMetafield, ratings, orderId, apiUrl, accessToken); } ratingStars.classList.remove('show'); } } }); }); });