Fetch Product Metafields

Solved

Fetch Product Metafields

func_dave
Visitor
2 0 1

Hello,

 

*Note: when I say metafield I mean a metafield created in Admin>Settings>Metafields

 

I've been trying to show nutritional info for a selected product that is included in a pack. The goal is to select a flavor from the options rendered by liquid, fetch the metafield data of the given product and then display it without reloading the page.

 

I see that it's not possible to retrieve metafield data for a product via the storefront API, not sure why because that would make this 1000% easier.

 

Here is the liquid for the dropdown:

 

<form>
  <div class="dropdown mt-4 mb-0">
    <label for="nutrition-dropdown">Nutrition Info</label>
    <button class="btn btn-lg bg-white btn-block text-left pl-1" id="nutrition-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span id="flavor-label">Select a Flavor</span><span class="text-muted float-right"><i class="fas fa-chevron-up"></i></span>
    </button>
    <div id="multi-flav-select" class="dropdown-menu w-100" aria-labelledby="nutrition-dropdown">
      {%- for flavor in flavors.included_items -%}
      <a class="dropdown-item pl-1" href="#" data-handle="{{ flavor.flavor_product_handle }}">{{ flavor.flavor_name }}</a>
      {%- endfor -%}
    </div>
  </div>
</form>

 

 

Here is the js running the logic behind the dropdown:

 

window.addEventListener('DOMContentLoaded', function() {
	setActiveSelectContainer('#multi-flav-select', '.dropdown-item');
});

function setActiveSelectContainer(containerSelector, subItemSelector) {
  let container = document.querySelector(containerSelector);
  let subItems = container.querySelectorAll(subItemSelector);

  for (var i = 0; i < subItems.length; i++) {
    subItems[i].addEventListener("click", function() {
      var current = document.querySelectorAll(".dropdown-item.active");

      if(current.length > 0) {
        current[0].className = current[0].className.replace(" active", "");
      }

      this.className += " active";
      
      setFlavorInfo(this.innerText, this.dataset.handle);
    });
  } 
}

function setFlavorInfo(currentFlavor, productHandle) {
  let flvLabel = document.querySelector('#flavor-label');
  flvLabel.innerText = currentFlavor;
  
  console.log("Current Product: " + currentFlavor + " | Product Data Handle:" + productHandle);
  
  /* grab flavor info async then display it on successful response  */
  const shopURL = '{{ shop.url }}';
  const urlForID = shopURL + '/products/' + productHandle + '.json';
  
  fetch(urlForID)
  .then(response => response.json())
  .then(function(data) {
    console.log("	--Product ID: " + data.product.id);
    getMetaFields(data.product.id);
  })
  .catch(err => console.log(err));
}

function getMetaFields(productID) {
  const shopURL = '{{ shop.url }}';
  const urlForMeta = shopURL + '/admin/products/' + productID + '/metafields.json';
  
  fetch(urlForMeta)
  .then(response => response.json())
  .then(function(data) {
    console.log("	--Product Metafields: " + data);
  })
  .catch(err => console.log(err))
}

 

 

I seem to be getting a CORS error on the second fetch request and I'm now wondering this: "Do I really have to create a private app to simply fetch metafields?"

 

Or am I approaching this wrong and there is another way to do this without creating an app?

 

The exact errors are as follow:

-TypeError: NetworkError when attempting to fetch resource.

-Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://my-store.com/admin/products/3486492852299/metafields.json . (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 401.

Accepted Solutions (2)

BenSehl
Shopify Staff
3 3 3

This is an accepted solution.

Hey Dave!

 

Have you seen this documentation? https://shopify.dev/custom-storefronts/products/metafields

 

If so, was that not what you were looking for? While you’re not able to create/update/delete metafields from the Storefront API — you are able to get/read them, which seems like is what is needed for your use case.

 

Here is the specific reference for the Product Metafield object: https://shopify.dev/api/storefront/2021-10/objects/Product#field-product-metafield

 

With regards to the private app — I’m quite sure you will need a storefront access token in order to access the Storefront API. You can set this up right in the admin: https://help.shopify.com/en/manual/apps/custom-apps#enable-custom-app-development-from-the-shopify-a...

 

Hope this helps!

Ben Sehl

View solution in original post

BenSehl
Shopify Staff
3 3 3

This is an accepted solution.

Yes, exactly! That Netlify outline is great. I think we have some of our own documentation around learning GraphQL — but that is a good source.

 

I understand it’s not the easiest learning curve, but it’s a very powerful tool when you can get the hang of it, and you minimize the size of the network request by only grabbing the fields you actually need.


Give it a shot and see where you get to. If you need more help, I’m happy to follow up with some examples when back at my computer.

 

Good luck!

Ben Sehl

View solution in original post

Replies 3 (3)

BenSehl
Shopify Staff
3 3 3

This is an accepted solution.

Hey Dave!

 

Have you seen this documentation? https://shopify.dev/custom-storefronts/products/metafields

 

If so, was that not what you were looking for? While you’re not able to create/update/delete metafields from the Storefront API — you are able to get/read them, which seems like is what is needed for your use case.

 

Here is the specific reference for the Product Metafield object: https://shopify.dev/api/storefront/2021-10/objects/Product#field-product-metafield

 

With regards to the private app — I’m quite sure you will need a storefront access token in order to access the Storefront API. You can set this up right in the admin: https://help.shopify.com/en/manual/apps/custom-apps#enable-custom-app-development-from-the-shopify-a...

 

Hope this helps!

Ben Sehl
func_dave
Visitor
2 0 1

Hey Ben,

 

Thanks for the quick response, I did see that documentation, but seeing that it used graphQL I thought it was meant to be used from an app and not from the front-end. The js I'm showing is a snippet I'm rendering into a product page. I'm not well versed with graphQL either by the way.

 

If I could I'd like to just use the initial fetch request and display the info from there, but it doesn't recognize 'data.product.metafields' when I try to use it, but does recognize 'data.product.id'. You're correct though I only want to display the metafields for reading and not for writing to.

 

Something like this:

 

 

function setFlavorInfo(currentFlavor, productHandle) {
  let flvLabel = document.querySelector('#flavor-label');
  flvLabel.innerText = currentFlavor;
  
  console.log("Current Product: " + currentFlavor + " | Product Data Handle: " + productHandle);
  
  const full_url = '/products/' + productHandle + '.json';
  
  fetch(full_url)
  .then(response => response.json())
  .then(function(data) {
    console.log("	--Product ID: " + data.product.id);
    console.log("	--Product Ingredients: " + data.product.metafields.nutrition.ingredients);
  })
  .catch(err => console.log(err));
}

 

 

*Edit: forgot to mention, it seems like I didn't need to provide a token to get the product id above. I'm also working with the live site up in another browser so I'm not signed in at all.

 

Thanks again, I appreciate the help a ton!

 

Edit 2: Would an approach using graphQL and fetch such as this work?

https://www.netlify.com/blog/2020/12/21/send-graphql-queries-with-the-fetch-api-without-using-apollo...

BenSehl
Shopify Staff
3 3 3

This is an accepted solution.

Yes, exactly! That Netlify outline is great. I think we have some of our own documentation around learning GraphQL — but that is a good source.

 

I understand it’s not the easiest learning curve, but it’s a very powerful tool when you can get the hang of it, and you minimize the size of the network request by only grabbing the fields you actually need.


Give it a shot and see where you get to. If you need more help, I’m happy to follow up with some examples when back at my computer.

 

Good luck!

Ben Sehl