Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Product Metafields #168

Closed
colinskow opened this issue Jul 12, 2016 · 43 comments
Closed

Product Metafields #168

colinskow opened this issue Jul 12, 2016 · 43 comments
Assignees

Comments

@colinskow
Copy link

Is there a way to access product metafields (described here) through the Javascript Buy SDK?

I am trying to add custom fields that show up in my custom cart, but I am not finding the metafields when I query products.

@richgilbank
Copy link
Contributor

Hi @colinskow,
Unfortunately they're not yet supported in the SDK, though maybe @lreeves or @minasmart can clarify if we'll be able to add support in the near future.

@lreeves
Copy link

lreeves commented Jul 12, 2016

Hey @colinskow we are definitely planning to include support for product metafields in an upcoming release - no date pinned yet though :-)

@lreeves lreeves self-assigned this Jul 12, 2016
@colinskow
Copy link
Author

@lreeves thank you very much for the response. In the mean time is there an API endpoint that I can call manually from the client browser to get that information? Thanks!

@lreeves
Copy link

lreeves commented Jul 13, 2016

Hm can you tell me how you've created the metafields and maybe give an example of one of the products? Also feel free to e-mail us at buybutton@shopify.com.

@yoadsn
Copy link

yoadsn commented Jul 14, 2016

I would love if the conversation continues here since I'm interest in the same aspects of a future and present support.
Thanks guys.

@joshwcomeau
Copy link

:(. My store absolutely needs this; I need some way of appending custom fields to products. I was just assuming that a call to fetchProduct would include that product's metafields...

I can work around it by using an object (or an ES6 Map) to connect product IDs to metafield data, and storing it locally... but it smells. Maintenance will be a pain.

Specifically, I'm building an online store that sells maps, with the ability to click on a map and find the map that covers that area. I'm storing lat and lng coordinates on each product using metafields, and hoping to do a simple calculation to find the closest map.

@homerjam
Copy link

homerjam commented Dec 7, 2016

+1

I need the alt text for images. Does anyone know of suggested workarounds or undocumented endpoints I could use in the interim? Thanks

@christianseel
Copy link

christianseel commented Dec 10, 2016

@lreeves It would be great if I would be able to add metadata to an order (e.g.) before the checkout. Do you think you'll be able to add metafields soon?

@minasmart
Copy link
Contributor

We're working on a major API refactor that'll sort this out. We'll have more info on timelines in the new year. Once I have a beta up and running and available, I'll post the details in this issue.

Thanks for checking in!

@RobEasthope
Copy link

@minasmart I appreciate it's only been a month or so but is there any news on timelines for the upcoming API refactor? Thanks!

@minasmart
Copy link
Contributor

Nope! I will be posting updates when they're available. :)

@alrick
Copy link

alrick commented Apr 24, 2017

@minasmart any news about metafields support?

@LAMike310
Copy link

Praying for an API update soon! Metafields with this SDK would be 💯

In the meantime I think I'll use the node-shopify library and build a simple API to call on the frontend to get that data.

@minasmart
Copy link
Contributor

Hey there! This isn't something we can really expose in a storefront context. If you need access to meta fields, my recommendation would be to use the admin API. Metadata can store just about anything, and some apps will store sensitive information in those metadata fields. Exposing those fields in an untrusted domain (storefront API, js buy SDK, etc) could put merchants at risk of exposing sensitive information, since most merchants are unaware of what data an app may store in arbitrary product metadata.

I'm closing this for now, since until we have better controls and permissions around metafields, we can't really take any action to expose this data.

Sorry all.

@alrick
Copy link

alrick commented Jun 16, 2017

@minasmart

Metafields have various use cases. For example, they can be used to further describe products or to store a "teaser" or "summary" for a blog post. You can also use metafields to share information between multiple Shopify applications.
https://help.shopify.com/api/reference/metafield

Okay, so we are encouraged to use metafield to store informations about products and can't use them with the js-buy-sdk.
This seems like non-sense to me.

@minasmart
Copy link
Contributor

You can use them in an authenticated domain, but the amount of data that some apps store in them makes them a pretty serious security issue to stick them in an unauthenticated domain.

I'm sorry you see this security concern as nonsense, but I'd remind you to abide by our code of conduct while participating in discussions here.

@homerjam
Copy link

We're working on a major API refactor that'll sort this out. We'll have more info on timelines in the new year. Once I have a beta up and running and available, I'll post the details in this issue.

I take it this was this abandoned in the refactor then?

better controls and permissions around metafields

Do you have any timelines on this?

Thanks

@alrick
Copy link

alrick commented Jun 16, 2017

Sorry if I offended you that wasn't my point, let's have a constructive discussion.

What I was trying to point out is that maybe using metafields to store sensitive informations isn't a good practice? And maybe Shopify could consider adding some sort of public metafields?

@minasmart
Copy link
Contributor

Yep! This is something we're aware of and working on. Unfortunately, we have a large number of shops and a large number of apps in our eco system. We can't make sweeping changes to something that's distributed through such a wide number of use cases.

When we have a workable solution that doesn't break everyone's shops and apps, we'll roll it out and document the change.

@drwpow
Copy link

drwpow commented Oct 21, 2017

@minasmart this is something I definitely needed for my store, but I appreciate your reasoning and understand the risk. It’s such a shame, though, because I’d like to be able to build full templates using React + React Router + JS Buy SDK, but without metafields support it’s not viable.

@drwpow
Copy link

drwpow commented Oct 22, 2017

One workaround would be to use Liquid’s json filter to write the metafields needed in a <script> tag that your JS app can read once the template loads. But this would bypass the entire JS Buy API entirely and make it impossible to build themes on your own machine.

@lapidus
Copy link

lapidus commented Sep 3, 2018

@minasmart: Any update on this issue? Would be very helpful with some way to access metafields ...

@minasmart
Copy link
Contributor

Unfortunately no. metafields can contain merchant facing or customer facing data, and there's currently no way for Shopify to know which is which. We can't expose metafields for all shops through the storefront API without exposing sensitive, merchant facing, information. The best solution for how to expose merchant facing data that you know is safe to expose, is to build your own app and API as a proxy, or hack theme files like what was suggested above.

If we figure out a solution that works across the board for whitelisting metafields that can be consumed by customers, I'll update here.

d3vhound referenced this issue in braxleybands/braxley-bands-storefront Oct 25, 2018
@lawrencetaur
Copy link

My client needs this as well. @minasmart one suggestion is to have public and private metafields, may solve your challenge and help others who are requesting it on the storefront api.

@lecoueyl
Copy link

lecoueyl commented May 7, 2019

That is not a solution but you can use the products tags as Metafields, you can add tags like meta:size:10

@lawrencetaur
Copy link

You can access via Storefront API https://help.shopify.com/en/api/custom-storefronts/storefront-api/guides/metafields
says that you can access a metafield but you must first whitelist it via Admin API.

@rebeccajfriedman
Copy link
Contributor

As of version 2.2.4, metafields are now available through custom queries in the JS Buy SDK as well.

@adamjw3
Copy link

adamjw3 commented Jul 26, 2021

@rebeccajfriedman can you show me how this works? i'm using shopify Product Reviews app and want to get them when getting products using JS Buy sdk, but no fields return relating to that.,

@riccardolardi
Copy link
Contributor

@adamjw3 or anyone done this and could share some sample code?

@jjaburke91
Copy link

jjaburke91 commented Dec 3, 2021

Wrestled with this extensively today to finally get a hold of metafields within my environment. I have directly queried the Storefront API to get the metafields instead, where the rest of my custom app is using the Shopify Buy SDK. However I did make progress in doing this with the Shopify SDK.

The following query should return the metafields via the custom query feature in the SDK, however it doesn't:

        const productsQuery = client.graphQLClient.query((root) => {
            root.addConnection('products', {args: {first: 10}}, (product) => {
                product.add('title');
                product.add('handle');

                product.addConnection('metafields', {args: {first: 10}}, (metafield) => {
                    metafield.add('id');
                    metafield.add('key');
                    metafield.add('value');
                })
            });
        });

        // Call the send method with the custom products query
        client.graphQLClient.send(productsQuery).then(({model, data}) => {
            // Do something with the products
        });

You can examine how the SDK translates this code into a GraphQL query via the browser network tabs. When copying that generated query and pasting into Postman, this query runs successfully but doesn't include any metafields. However, when changing the generated query from a single line query to one that is new-lined to match general style practices, the query returns the metafields!!


Working around this bug. I am instead querying the Storefront API directly to get the metafields and parsing that response manually. Quick summary of how I did that here:

// function creating post response to storefront API
async function requestMetafieldsData (productHandle) {
    var postUrl = `https://${STORE_DOMAIN}/api/2021-10/graphql.json`;

    const requestBody = {
    	'async': true,
    	'crossDomain': true,
    	'method': 'POST',
    	'headers': {
    		'X-Shopify-Storefront-Access-Token': STOREFRONT_ACCESS_TOKEN,
    		'Content-Type': 'application/graphql',
    	},
    	'body': getProductByHandle(productHandle),
    }

    const response = await fetch(postUrl, requestBody);

    if (!response.ok) {
        console.error(`Request failed with status ${response.status}`)
    }

    console.log("Request successful!");

    return await response.json();
}

// Graphql query string to retrieve metafields
export const getProductByHandle = (productHandle) => `
query {
    product(handle: "${productHandle}") {
        id
        handle
        metafields (first: 20) {
            edges {
                node {
                    key,
                    value
                }
            }
        }
    }
}
`;

Notes:

  • You must configure storefront to display the metafield first. Guide on this here https://shopify.dev/custom-storefronts/products/metafields
  • Storefront API and Admin API are different and have different security settings in Shopify for what they be queried for. Be cautious of all the API calls you see having /api/2021-10/graphql.json or /admin/api/2021-10/graphql.json - they are different and require slightly http headers for authentication.
  • Not all the API versions offer the same features (obviously), make sure you're using the right one when reading docs online.
  • I ran into CORS issues on this. Be sure to use the 'crossDomain' option in the fetch request.

Some useful links:

Hopefully enough there to help anybody else struggling with this.

Far too difficult a task for something as common as metafields, this functionality should be added to the Shopify SDK API without a doubt.

@riccardolardi
Copy link
Contributor

riccardolardi commented Dec 3, 2021

@jjaburke91 thanks for your update. Me too I spent quite some time today working on this. Weirdly, for me this works fine:

      const metafieldQuery = client.graphQLClient.query((root) => {
        root.addConnection('products', { args: { first: 249 } }, (product) => {
          product.add('handle')
          product.addConnection(
            'metafields',
            { args: { first: 249 } },
            (metafield) => {
              metafield.add('key')
              metafield.add('value')
            }
          )
        })
      })
      const metafieldQueryResult = await client.graphQLClient.send(
        metafieldQuery
      )

What doesn't is just querying for one single product, which actually was my original goal. If querying for product instead of products I get an error about pageInfo not being found in the schema #864

@jjaburke91
Copy link

jjaburke91 commented Dec 3, 2021

@riccardolardi Double check the API version you're using on that call to storefront, I had the same issue and spotted my version was a couple of months old. api/2021-10 you should be using I believe. I think the product call to storefront API is quite recent? Wasn't available on version 2021-07 if I remember correctly.

Interesting your version of that works, will come back and try my version of that some point.

@riccardolardi
Copy link
Contributor

@jjaburke91 any update from your side on this? With Storefront API update 2022-10 the approach mentioned above has been deprecated: https://shopify.dev/changelog/the-behavior-of-hasmetafields-metafields-has-changed

@jjaburke91
Copy link

Hey @riccardolardi . I've just returned to this problem for the first time since I last touched Metafields stuff.

Did you find any solution to access Metafields similar to above?

@riccardolardi
Copy link
Contributor

riccardolardi commented Feb 13, 2023

Hi @jjaburke91 I'm sorry this has been a while now for me too so I'm a bit out of context but this is what I've ended up with back in the days:

const metafieldQuery = $shopify.graphQLClient.query((root) => {
  root.addConnection('products', { args: { first: 249 } }, (product) => {
    product.add('handle')
    product.add(
      'metafields',
      {
        args: {
          identifiers: [{ key: 'has_textfield', namespace: 'my_fields' }],
        },
      },
      (metafield) => {
        metafield.add('namespace')
        metafield.add('key')
        metafield.add('value')
      }
    )
  })
})
const metafieldQueryResult = await $shopify.graphQLClient.send(
  metafieldQuery
)

Hope this helps.

@jjaburke91
Copy link

jjaburke91 commented Feb 13, 2023

Great I'll have a go with that. Thanks for speedy reply.

Could you confirm which versions of the APIs you were using?

@riccardolardi
Copy link
Contributor

@jjaburke91 I think 2022-10

@skeddles
Copy link

Spent a few hours trying to resolve this (everything was returning null or breaking my query), until I found the permission it was missing:

On each field you want to retrieve, you have to check off "Storefronts" in it's configuration:
firefox_2023-05-17_15-55-45

I used @riccardolardi's code above, and as soon as I checked that off it started working.

(just adding this in case someone else ends up here)

@jacobmellin
Copy link

jacobmellin commented Nov 6, 2023

Hi all, when trying to use metafields through a custom query, I am getting the errror: Error: No field of name "metafield" found on type "Product" in schema.

root.addConnection('products', { args: { first: 100 } }, (product: any) => {
            product.add('id');
            product.add('handle');
            product.add('title');
            product.add('productType');
            product.add('description');
            product.add('descriptionHtml');
            product.add('availableForSale');
            product.add('metafield', { args: { identifiers: [{namespace: 'custom', key: 'etikett_pfeildingsie_' }] } }, (metafield: any) => {
                metafield.add('key');
                metafield.add('value');  
            });
            product.addConnection('images', { args: { first: 100 } }, (image: any) => {
                image.add('id');
                image.add('altText');
                image.add('url');
            });
            product.addConnection('variants', { args: { first: 100 } }, (variant: any) => {
                    variant.add('id');
                    variant.add('title');
                    variant.add('price', (price: any) => {
                        price.add('amount');
                        price.add('currencyCode');
                    });
                    variant.add('compareAtPrice', (compareAtPrice: any) => {
                        compareAtPrice.add('amount');
                        compareAtPrice.add('currencyCode');
                    });
            });
        });

Is there a way to extend the schema with the necessary types without replacing it entirely on my end?

@ViktorShev
Copy link

Currently experiencing the same issue as @jacobmellin.

API version: 2024-01
SDK version: ^2.21.1

@jazsouf
Copy link

jazsouf commented Mar 20, 2024

I believe it's "metafields" in plural for the query connection to work.

@nathancahill
Copy link

Still not working with 2024-01.

@nathancahill
Copy link

In shopify-buy@2.20.0 the allowed product fields are:

var Product = {
  "name": "Product",
  "kind": "OBJECT",
  "fieldBaseTypes": {
    "availableForSale": "Boolean",
    "createdAt": "DateTime",
    "description": "String",
    "descriptionHtml": "HTML",
    "handle": "String",
    "id": "ID",
    "images": "ImageConnection",
    "onlineStoreUrl": "URL",
    "options": "ProductOption",
    "productType": "String",
    "publishedAt": "DateTime",
    "title": "String",
    "updatedAt": "DateTime",
    "variants": "ProductVariantConnection",
    "vendor": "String"
  },
  "implementsNode": true
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests