Shopify / js-buy-sdk

The JS Buy SDK is a lightweight library that allows you to build ecommerce into any website. It is based on Shopify's API and provides the ability to retrieve products and collections from your shop, add products to a cart, and checkout.
https://shopify.github.io/js-buy-sdk
MIT License
989 stars 261 forks source link

Product Metafields #168

Closed colinskow closed 7 years ago

colinskow commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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

colinskow commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

:(. 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 commented 7 years ago

+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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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

alrick commented 7 years ago

@minasmart any news about metafields support?

LAMike310 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 6 years ago

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

minasmart commented 6 years ago

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.

lawrencetaur commented 5 years ago

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 commented 5 years ago

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

lawrencetaur commented 5 years ago

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 commented 5 years ago

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

adamjw3 commented 3 years ago

@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 commented 2 years ago

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

jjaburke91 commented 2 years ago

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:

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 commented 2 years ago

@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 https://github.com/Shopify/js-buy-sdk/issues/864

jjaburke91 commented 2 years ago

@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 commented 2 years ago

@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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

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

riccardolardi commented 1 year ago

@jjaburke91 I think 2022-10

skeddles commented 1 year ago

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 commented 11 months ago

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 commented 7 months ago

Currently experiencing the same issue as @jacobmellin.

API version: 2024-01 SDK version: ^2.21.1

jazsouf commented 7 months ago

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