Shopify Storefront API: Getting referenced variant with GraphQL - graphql

I'm using the Shopify Storefront API and Accentuate to try to get my hands on a specific variant, but it won't work for me.
THE SHORT VERSION: When I select a variant on the website, I get the url: (... url ...)?variant=31696763027492. How do I get my hands on these numbers after the = for the variant in GraphQL? It does not match the ID.
THE LONG VERSION...
In a product variant, I reference to a variant of another product (with Accentuate). What I need to get out is the variant that I am referencing to.
When I pull out the variant that is being referenced to in the product (as metafield, with GraphQL), I get this:
{
"key": "products_in_package",
"value": "pakke-produkt-gavepose:31696763027492"
}
My problem is the numbers after : in the value. I've found that these are the number that come after the URL of the product when selecting the variant on the "actual" Shopify website ((... url ...)?variant=31696763027492), but I can't see how I can use them, since I can't find these numbers on the actual variant through GraphQL.
It does not match the 'id' or anything else I could find on the variant. Neither can I include the numbers if I try to get productByHandle.
So, does anyone have any ideas on how I can I use it to get the actual product variant through GraphQL? Or ideas on what else I can do to connect a specific product variant to another product variant?

The storefront API and admin API return different format of Id
storefront API: "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzQ3NjA3ODY1MDE3MDM="
Admin API: "gid://shopify/Product/4760786501703"
But there is a convert way between each other, use function atob
atob("Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzQ3NjA3ODY1MDE3MDM=") will get the Admin API Id
For NodeJS package: https://www.npmjs.com/package/atob

The variant ID and GraphQL variant ID is closely related.
Variant id - 31646396055604
GraphQL Variant id - gid://shopify/ProductVariant/31646396055604
The GraphQL ID is the same as the Variant id but you must add the string gid://shopify/ProductVariant/ before it.
So your GraphQL request will become like so:
{
productVariant(id:"gid://shopify/ProductVariant/31646396055604"){
title
product{
id
}
}
}

The problem is (or was) that those numbers are not what you get when you get the id from the product through GraphQL. You get a much longer id, like "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC8zMTY5NDA3MjQxNDI0NA==".
However, I figures out that using the btoa() JavaScript function on the id you mention (gid://shopify/ProductVariant/31646396055604), I got the long id that I get from the product itself. So problem solved!

Related

Shopify GraphQL Pagination Without Using Cursor

I have read the tutorial for pagination using the parameter after:cursor in this article https://shopify.dev/concepts/graphql/pagination. Unfortunately, I cannot use this because I want to generate a sitemap for all the products. As you know, we can only get 250 products maximum at a single graphql request. What I am building is something like this:
sitemaps/products-1-250.xml
sitemaps/products-251-500.xml
sitemaps/products-501-750.xml
and so on...
I want to make some query like this where I can get products from 251 to 500:
query GetAllProductsForSitemap {
products(first: 250, page: 2) {
edges {
node {
id
title
other-attributes
}
}
}
}
But of course, page is not a valid parameter.
Is this achievable in Shopify-GraphQL?
Why don't you just take the perfect sitemap for all your products that are generated for free for you by Shopify? Any reason you are re-inventing the wheel?
As for your request, the short answer is no. One uses cursors when using the GraphQL API for data. Alternatively, you are 100% free to use paging with the RestAPI, so if you have to, you can.

How to query data from 2 APIs

I have setup a Gatsby Client which connects to Contentful using the gatsby-source-contentful plugin. I have also connected a simple custom API which is connected using the gatsby-source-graphql plugin.
When I run the dev-server I am able to query my pages from Contentful in the playground.
I am also able to query my custom API through the playground as well.
So both APIs work and are connected with Gatsby properly.
I want to programatically generate a bunch of pages that have dynamic sections (references) which an author can add and order as she wishes.
I do achieve this using the ...on Node connection together with fragments I define within each dynamic section. It all works out well so far.
My actual problem:
Now I have a dynamic section which is a Joblist. This Component requires to get data out of the Contentful API as it stores values like latitude and longitude. So the author is free to set a point on a map and set a radius. I successfully get this information out of Contentful using a fragment inside the component:
export const query = graphql `
fragment JoblistModule on ContentfulJoblisteMitAdresse {
... on ContentfulJoblisteMitAdresse {
contentful_id
radius
geo {
lon
lat
}
}
}`
But how can I pass this information in to another query that fetches the jobdata from my custom API? If I understand Gatsby correctly I somehow have to connect these two API's together? Or can I run another query somehow that fetches these values passed in as variables? How and where would I achieve this?
I could not find any approach neither inside the gatsby-node.js (since passed-in context can only be used as variables inside a query) nor in the template-file (since I can run only 1 query at a time), nor in the component itself (since this only accept staticQuery)
I don't know where my misunderstanding is. So I would very appreciate any hints, help or examples.
Since your custom API is a graphQL API, you can use delegateToSchema from the graphql-tools package to accomplish this.
You will need to create a resolver using Gatsby's setFieldsOnGraphQLNodeType API. Within this resolver, your resolve function will call delegateToSchema.
We have a similar problem, our blog posts have an "author" field which contains an ID. We then do a graphQL query to another system to look up author info by that ID.
return {
remoteAuthor: {
type: person,
args: {},
resolve: async (source: ContentfulBlogPost, fieldArgs, context, info) => {
if (!source.author) {
return null
}
// runs the selection on the remote schema
// https://github.com/gatsbyjs/gatsby/issues/14517
return delegateToSchema({
schema: authorsSchema,
operation: 'query',
fieldName: 'Person',
args: { id: source.author },
context,
info,
})
},
},
}
This adds a 'remoteAuthor' field to our blog post type, and whenever it gets queried, those selections are proxied to the remote schema where the person type exists.

Apollo Client: can apollo-link-rest resolve relations between endpoints?

The rest api that I have to use provides data over multiple endpoints. The objects in the results might have relations that are are not resolved directly by the api, it rather provides ids that point to the actual resource.
Example:
For simplicity's sake let's say a Person can own multiple Books.
Now the api/person/{i} endpoint returns this:
{ id: 1, name: "Phil", books: [1, 5, 17, 31] }
The api/book/{i} endpoint returns this (note that author might be a relation again):
{ id: 5, title: "SPRINT", author: 123 }
Is there any way I can teach the apollo client to resolve those endpoints in a way that I can write the following (or a similar) query:
query fetchBooksOfUser($id: ID) {
person (id: $id) {
name,
books {
title
}
}
}
I didn't try it (yet) in one query but sould be possible.
Read docs strating from this
At the beggining I would try with sth like:
query fetchBooksOfUser($id: ID) {
person (id: $id) #rest(type: "Person", path: "api/person/{args.id}") {
name,
books #rest(type: "Book", path: "api/book/{data.person.books.id}") {
id,
title
}
}
}
... but it probably won't work - probably it's not smart enough to work with arrays.
UPDATE: See note for similiar example but using one, common parent-resolved param. In your case we have partially resolved books as arrays of objects with id. I don't know how to use these ids to resolve missing fields () on the same 'tree' level.
Other possibility - make related subrequests/subqueries (someway) in Person type patcher. Should be possible.
Is this really needed to be one query? You can provide ids to child containers, each of them runing own query when needed.
UPDATE: Apollo will take care on batching (Not for REST, not for all graphql servers - read docs).
'it's handy' to construct one query but apollo will cache it normalizing response by types - data will be stored separately. Using one query keeps you within overfetching camp or template thinking (collect all possible data before one step rendering).
Ract thinking keeps your data and view decomposed, used when needed, more specialised etc.
<Person/> container will query for data needed to render itself and list of child-needed ids. Each <Book/> will query for own data using passed id.
As an alternative, you could set up your own GraphQL back-end as an intermediary between your front-end and the REST API you're planning to use.
It's fairly easy to implement REST APIs as data sources in GraphQL using Apollo Server and a package such as apollo-datasource-rest which is maintained by the authors behind Apollo Server.
It would also allow you to scale if you ever have to use other data sources (DBs, 3rd party APIs, etc.) and would give you full control about exactly what data your queries return.

Magento rest api: how to get orders by ids?

How to get multiple orders filtered by Id from magento with help of rest api?
I tryed to use http://192.168.0.104:4422/magento/api/rest/orders?filter[1][attribute]=entity_id&filter[1][in]=1&filter[1][in]=3
but it returns me:
<magento_api>
<messages>
<error>
<data_item>
<code>401</code>
<message>oauth_problem=signature_invalid</message>
</data_item>
</error>
</messages>
</magento_api>
But if i use request with only one filter in parameter http://192.168.0.104:4422/magento/api/rest/orders?filter[1][attribute]=entity_id&filter[1][in]=1 - it works great.
For me the problem was in filters( I tried on 1.7.0.1). Looks like some magento versions have problems with filters. So you should use 1 filter, after that filter results in your code.
I found the answer in this blog post. In short, if you follow this structure (this was for my use case with a list of products):
http://magentohost.com/api/rest/products?filter[1][attribute]=entity_id&filter[1][in][1]=1&filter[1][in][2]=3
You'll be set! (this gets two products similar to above with ids 1 and 3). Notice the [1] and [2] after both of the [in]. I wish Magento had better documentation for this.
Try aggregating values.
complexFilter { key = "increment_id", value = new associativeEntity {key = "in", value = "1,2,3"} };

Magento 1.6.0 API product update not working when using numerical SKU

My products all use numerical SKUs, but it seems to cause a problem when using the API to do product update.
According to the API doc, you can use either product ID or SKU.
Arguments:
mixed product - product ID or Sku
array productData - array of attributes values
mixed storeView - store view ID or code (optional)
But fully numerical SKUs don't seem to work.
I'm convinced there is some code somewhere which checks if the value is numerical and assumes I must be supplying the product ID.
I also read somewhere you can pass in a 4th parameter to specify you are using sku, but that didn't work either.
$proxy->call($sessionId, 'product.update', array('123456', array('name'=>'Updated name1'), null, 'sku') );
Does anyone know how to get this working?
Short answer is that there's a bug somewhere preventing the last param of product.update from being set properly (or maybe Varien haven't yet implemented it), which also presents a problem for the method product.info.
A quick workaround (if you don't mind losing the option to update by ID) is just to set the $identifierType in the Product API update() method ):
In app/code/core/Mage/Catalog/Model/Product/Api.php l.198
public function update($productId, $productData, $store = null, $identifierType = 'sku')
And finally load the product within the if ($idBySku) condition of the method getProduct() around l.427 of app/code/core/Mage/Catalog/Helper/Product.php
$productId = $idBySku;
$product->load($productId);
It's a bit of a fudge. I'll have a look for a better workaround as an override; otherwise, maybe someone else can post a better solution.
In a similar question TurmDrummer posted another workaround:
https://stackoverflow.com/a/10915276/1598270
There is a workaround for pure numeric or mixed SKU, that works quiet
well for me.
Just add a whitespace at the end of your SKU. Magento will interpret
the value as a SKU, because whitespace is non numeric. Internaly
Magento trims the whitespace later
This works perfectly from Magento 1.4.x - 1.7.
I liked this solution a bit better as a workaround because you aren't modifying any core code files, which I try to avoid.

Resources