Discussing Shopify Functions development, deployment, and usage in Shopify apps.
Hi everyone,
I am attempting to create multiple discounts using the 'discountAutomaticAppCreate' GraphQL function and add metafields with the same key and namespace. This process has been successful and I have created 2 automatic discount. However, when I try to access the metafield in the 'run.graphql' file, I only retrieve the metafield of the latest discount node.
Therefore, my question is: How can I access the metafields of multiple discount nodes?
Is I'm missing somthing? Please help with me out with some clarification.
@Nick_Wesselman @Liam @SomeUsernameHe
Kindly check the below code.
query RunInput($selectedCollectionIds: [ID!]) {
cart {
lines {
quantity
merchandise {
__typename
... on ProductVariant {
id
product {
id
inAnyCollection(ids: $selectedCollectionIds)
}
}
}
}
}
discountNode {
metafield(
key: "test-function-config"
namespace: "test-discount"
) {
value
}
}
}
Thanks
To access the metafields of multiple discount nodes in Shopify using GraphQL, you need to modify your query to include each discount node you're interested in. From the provided query, it seems you're trying to access a single metafield from a generic discountNode which would typically return the metafield from the latest or default discount node.
To fetch metafields from multiple discounts, you must query each discount node separately. Here's how you can modify your query to retrieve the metafields from multiple discount nodes by specifying their IDs:
query GetDiscountMetafields($discountIds: [ID!]!) {
nodes(ids: $discountIds) {
... on DiscountNode {
id
metafield(namespace: "test-discount", key: "test-function-config") {
value
}
}
}
}
In this modified query, you provide a list of discount IDs as an input variable $discountIds. The query uses these IDs to fetch the metafield for each DiscountNode. This approach allows you to access the metafields of each discount individually based on the IDs you specify.
Assuming you know the specific IDs of the discounts whose metafields you want to access, you can pass them in the query like this:
{
"discountIds": ["gid://shopify/DiscountNode/1234567890", "gid://shopify/DiscountNode/0987654321"]
}
This method ensures that you retrieve the metafields from all specified discounts, allowing for more precise control over which discounts' metafields are accessed
Sorry, was not able to test this code completely since I am heading to bed, but if you still need help with it I'm a bit, I will troubleshoot when I wake up
The question is about Functions, not the Admin API.
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Hi @yedla_dinesh --
I may not understand your goals fully, but in a given function execution, you can only obtain the metafields for a single discount. You can reuse the same functionId across multiple discounts, and they should both execute independently and in parallel, with their own metafields. They may not both apply to the cart, depending on the discount combination rules.
-Nick
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Thanks for your reply.
I understand that in a single function execution, I can only obtain the metafields for a single discount. However, I want to access the metafields for multiple discounts in one go.
Let's say I've created 2-3 discounts using the 'discountAutomaticAppCreate' GraphQL function (and I've also added metafield data to each discount). Now, can I access all the discount metafield values in the Shopify product discount function (run.graphql) input query?
I really appreciate your help!
Thanks
Hi @yedla_dinesh --
No, this is not possible. Each discount you create with discountAutomaticAppCreate represents a separate execution of your function. They will all be executed in parallel and the discounts will be combined according to Shopify's discount combination rules. Each execution can only access the metafields of a single discount.
If you are looking for some sort of shared configuration, you might consider a shop metafield. Otherwise the best practice is to configure and output the discounts independently from each function run, and allow Shopify to choose the best discount combination for the shopper.
-Nick
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
In the same example suppose multiple discounts are created how can I pass a discount id to the run.graphql query?
query RunInput {
discountNode {
metafield(namespace: "can I make dynamic and have it use the discount id?", key: "function-configuration") {
value
}
}
}
https://shopify.dev/docs/apps/build/functions/input-output/use-variables-input-queries
You pass everything to run.graphql through input variables.
Thank you! The documentation is slightly confusing. In Step 2 it says to create the fields in metafield-values.json
But then below it says to set them in set-metafield.graphql mutation.
Then in step 3 it says to load them via the extensions toml like so:
[extensions.input.variables]
namespace = "$app:my-namespace"
key = "my-key"
What is the purpose of the metafield-values.json if they have to be manually set in the set metafield?
Yeah, that can be a little confusing.
metafield-values.json is not something you create. The box referencing metafield-values.json is just showing you how your values should look when saving them with the "mutation SetMetafield"
mutation SetMetafield {
metafieldsSet(metafields: [
{
namespace: "$app:my-namespace",
key: "my-key",
ownerId: "OWNER_ID",
type: "json",
value: "{\"excludedCollectionIds\"=>[\"gid://shopify/Collection/1\", \"gid://shopify/Collection/2\", \"gid://shopify/Collection/3\"], \"vipCollectionIds\"=>[\"gid://shopify/Collection/4\", \"gid://shopify/Collection/5\"]}{\"selectedCollectionIds\":[\"gid://shopify/Collection/1\",\"gid://shopify/Collection/2\",\"gid://shopify/Collection/3\"]}"
}
]) {
metafields {
id
}
}
}
Ok got it. Suppose I have one function but I have multiple discounts. (As stated above originally in the first question) is there away I can make the name space dynamic? Currently the way it looks there's no dynamic way to look up data. Dynamic in this context would be just me setting the variable fields but how would I be able to have it actually run the dynamic data when the function runs?
I am not quite sure I understand what you are asking.
"no dynamic way to look up data" That is what you are doing with graphql.run. That is the entire point of graphql.run is that is runs and pulls all relevant information you need for run.js to run and apply the discount.
I'm sorry I didn't explain it correctly. Here is what I mean. I have one function called product-discount.
This function creates a discount. Suppose the function creates:
Discount1, Discount2, Discount3
Each one of these discounts gives a discount based on a product tag.
In the run.graphql I have
query RunInput($prodTags: [String!]) {
cart {
lines {
id
quantity
merchandise {
... on ProductVariant {
id
product {
hasAnyTag(tags: [$prodTags])
}
}
__typename
}
}
}
}
But how could I actually pass a product tag to run.graphql? I could manually input it but I need it to be dynamic because there are multiple discounts. Is this even possible?
So here is how I pass tags to my product discount functinon.
Here is my graphql.run:
query Input($selectedCollectionIds: [ID!], $tags: [String!]) {
cart {
lines {
merchandise {
__typename
... on ProductVariant {
id
product {
handle
inCollections(ids: $selectedCollectionIds) {
collectionId
isMember
}
}
}
}
}
buyerIdentity {
customer {
hasTags(tags: $tags) {
tag
hasTag
}
}
}
}
shop {
metafield(namespace: "simplewholesale", key: "tags") {
value
}
}
}
In this code, I pass my "tags" that I use for discounts with:
hasTags(tags: $tags)
This looks in the metafield we created earlier:
mutation SetMetafield {
metafieldsSet(metafields: [
{
namespace: "$app:my-namespace",
key: "my-key",
ownerId: "OWNER_ID",
type: "json",
value: "{\"excludedCollectionIds\"=>[\"gid://shopify/Collection/1\", \"gid://shopify/Collection/2\", \"gid://shopify/Collection/3\"], \"vipCollectionIds\"=>[\"gid://shopify/Collection/4\", \"gid://shopify/Collection/5\"]}{\"selectedCollectionIds\":[\"gid://shopify/Collection/1\",\"gid://shopify/Collection/2\",\"gid://shopify/Collection/3\"]}"
}
]) {
metafields {
id
}
}
}
Which for my discount has something like this:
{
"selectedCollectionIds": [
"gid://shopify/Collection/310827712556"
],
"tags": [
"Pro",
"VIP"
]
}
^ So in this metafield is where I save all of my "dynamic content" for my graphql. Then in run.graphql you can call a specific "selectedCollectionIds" or "tags" by selecting the variable you want to use, in my case it will be "id" then the option name in your metafield which will be "selectedCollectionIds" and the code will look like this:
ids: $selectedCollectionIds
Now,
hasTags(tags: $tags)
I had AI reword my reply a little more technical since I am in a little bit of a rush, but this should be a little more clear:
In my Shopify Product Discount Function, I dynamically pass tags and collection IDs to apply discounts based on customer tags and product collections. Here’s how the process works using GraphQL queries and Shopify Metafields:
I use the following GraphQL query to retrieve information about the products in the cart, as well as the tags associated with the customer:
query Input($selectedCollectionIds: [ID!], $tags: [String!]) {
cart {
lines {
merchandise {
__typename
... on ProductVariant {
id
product {
handle
inCollections(ids: $selectedCollectionIds) {
collectionId
isMember
}
}
}
}
}
buyerIdentity {
customer {
hasTags(tags: $tags) {
tag
hasTag
}
}
}
}
shop {
metafield(namespace: "simplewholesale", key: "tags") {
value
}
}
}
The dynamic values for the tags and collections used in the query are stored in a Shopify Metafield. Here’s an example of how I set the metafield using a GraphQL mutation:
mutation SetMetafield {
metafieldsSet(metafields: [
{
namespace: "$app:my-namespace",
key: "my-key",
ownerId: "OWNER_ID",
type: "json",
value: "{\"selectedCollectionIds\":[\"gid://shopify/Collection/310827712556\"],\"tags\":[\"Pro\",\"VIP\"]}"
}
]) {
metafields {
id
}
}
}
The metafield stores the collections and tags in a JSON structure like this:
{
"selectedCollectionIds": [
"gid://shopify/Collection/310827712556"
],
"tags": [
"Pro",
"VIP"
]
}
This JSON object is used to dynamically manage discount logic. The selectedCollectionIds array contains collection IDs that the product might belong to, and the tags array stores customer tags (like "Pro" and "VIP").
In my run.graphql query, I pass variables from the metafield data to dynamically check for product collections and customer tags. Specifically:
inCollections(ids: $selectedCollectionIds): This checks whether products in the cart are part of any of the collections in the selectedCollectionIds array from the metafield.
hasTags(tags: $tags): This checks if the customer has any of the tags specified in the tags array from the metafield.
In this setup, the dynamic content (like collection IDs and customer tags) is stored in a metafield as a JSON object. I use that metafield to pass values to the GraphQL query for determining which collections a product belongs to and which tags a customer has. This allows the discount function to apply discounts based on the customer's tags and the product's collection membership.
This structure makes it easy to dynamically update the tags and collection IDs in the metafield without modifying the core function logic, ensuring flexibility in applying discounts.
SO AWESOME! THANK YOU!!!!
You are welcome! Happy coding!
Thank you for your amazing help I had one more question. How does run.graphql know to reference the metafields for the variables?
in example::
query Input($selectedCollectionIds: [ID!], $tags: [String!]) {
Correct me if I'm wrong but would they be added to the shopify.extension.tom?
If so would it be:
namespace = "$app:my-namespace",
key = "my-key"
Of course, no problem.
Correct, you would add the following to your shopify.extension.toml:
[extensions.input.variables]
namespace = "$app:my-namespace"
key = "my-key"
This references the metafield that run.graphql will check for selectedCollectionIds or tags per the example above. You can only have 1 here. So that is why you put everything you need in this one single metafield.
I had one more question so sorry!
I'm passing this value (this is from graphql) "value": "{\"selectedCollectionIds\":[\"gid://shopify/Collection/310827712556\"],\"tags\":[\"CL Og test\",\"VIP234234\"]}",
When I run
What was your solution or work around?