Migrate your app from REST to GraphQL
GraphQL is a query language and a runtime system. While REST offers multiple endpoints that return fixed data structures, GraphQL only exposes a single endpoint and allows you to navigate a graph of related data or fetch resources directly by their ID.
This tutorial covers the following topics:
- Making requests to the REST Admin API for GraphQL object IDs
- Combining multiple REST Admin API requests into a single GraphQL query
- Switching from page-based to cursor-based pagination
Requirements
- You've completed our Getting started with the GraphQL Admin and REST Admin APIs and Getting started with the Storefront API guides and you're authenticated with the APIs.
- You've set up a Shopify partner account.
- You've set up a development store with product, customer, and order data that you can use to test your app.
Retrieve GraphQL object IDs
To migrate to GraphQL, you need to query the GraphQL object IDs that correspond to REST resource IDs. You can retrieve GraphQL IDs for both the GraphQL Admin API and the GraphQL Storefront API.
GraphQL Admin API IDs
To help with migrating from the REST Admin API to the GraphQL Admin API, all REST responses include the admin_graphql_api_id
property. You can use the ID value in this property to query the object directly using the GraphQL Admin API.
The following example retrieves a product variant in REST, and then uses the admin_graphql_api_id
property to query the product variant in GraphQL.
REST request
GET /admin/api/2021-01/products/1321540747320/variants/12195007594552.json
JSON response
{
"variant": {
"id": 12195007594552,
"product_id": 1321540747320,
"title": "Default Title",
"...": "...",
"admin_graphql_api_id": "gid://shopify/ProductVariant/12195007594552"
}
}
GraphQL query
POST /admin/api/2021-01/graphql.json
{
productVariant (id: "gid://shopify/ProductVariant/12195007594552") {
id
title
}
}
JSON response
{
"data": {
"productVariant": {
"id": "gid://shopify/ProductVariant/12195007594552",
"title": "Default Title"
}
},
}
Anatomy of a GraphQL ID
Always treat the admin_graphql_api_id
string as an opaque ID.
GraphQL ID generation is implementation dependent and doesn't follow any convention other than being a
URI. There's no guarantee that GraphQL IDs will follow a structure (gid
-> shopify
-> resource
-> rest_id
) that's similar to the previous example, so you shouldn't generate IDs programatically.
The following example shows how the admin_graphql_api_id
property doesn't always follow an expected structure.
REST request
GET /admin/api/2021-01/inventory_levels.json?inventory_item_ids=12261979488312
JSON response
{
"inventory_levels": [
{
"inventory_item_id": 12261979488312,
"location_id": 6884556842,
"available": 5,
"updated_at": "2018-05-17T12:58:30-04:00",
"admin_graphql_api_id": "gid://shopify/InventoryLevel/6485147690?inventory_item_id=12261979488312"
},
{
"inventory_item_id": 12261979488312,
"location_id": 13968834616,
"available": 7,
"updated_at": "2018-05-17T12:58:35-04:00",
"admin_graphql_api_id": "gid://shopify/InventoryLevel/13570506808?inventory_item_id=12261979488312"
},
{
"inventory_item_id": 12261979488312,
"location_id": 13968867384,
"available": 9,
"updated_at": "2018-05-17T12:58:35-04:00",
"admin_graphql_api_id": "gid://shopify/InventoryLevel/13570539576?inventory_item_id=12261979488312"
}
]
}
GraphQL Storefront API IDs
The Storefront API is also queried using GraphQL, and it uses its own separate IDs. The GraphQL Admin API provides the storefrontId
field, which you can use to get an object's Storefront API ID.
The following example shows how to request the storefrontId
field on a product.
GraphQL query
POST /admin/api/2021-01/graphql.json
{
product(id:"gid://shopify/Product/1324932956216") {
id
title
storefrontId
}
}
JSON response
{
"data": {
"product": {
"id": "gid://shopify/Product/1324932956216",
"title": "GQL Lego Connector",
"storefrontId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMjQ5MzI5NTYyMTY="
}
},
}
Combine multiple REST requests into a single GraphQL query
In GraphQL, you can use connections to get information about related objects with a single request. For example, for an Order, you might want to know the total price set, the customer's name, metafields, and the title of other variants belonging to the product in the order.
Using REST, you need to make a request to the following endpoints and filter out unnecessary data:
/admin/api/2021-01/orders/{order_id}.json
/admin/api/2021-01/products/{product_id}/variants.json
/admin/api/2021-01/customers/{customer_id}/metafields.json
Using GraphQL, you can make a single request using connections to get your desired data:
GraphQL query
POST /admin/api/2021-01/graphql.json
{
order(id:"gid://shopify/Order/1938703319062") {
id
totalPriceSet {
presentmentMoney {
amount
currencyCode
}
}
customer {
displayName
metafields (first:10) {
edges {
node {
namespace
key
value
}
}
}
}
lineItems (first:10) {
edges {
node {
variant {
product {
variants(first:10) {
edges {
node {
title
}
}
}
}
}
}
}
}
}
}
JSON response
{
"data": {
"order": {
"id": "gid://shopify/Order/1938703319062",
"totalPriceSet": {
"presentmentMoney": {
"amount": "7.99",
"currencyCode": "CAD"
}
},
"customer": {
"displayName": "John Smith",
"metafields": {
"edges": [
{
"node": {
"namespace": "instructions",
"key": "wash",
"value": "cold wash"
}
}
]
}
},
"lineItems": {
"edges": [
{
"node": {
"variant": {
"product": {
"variants": {
"edges": [
{
"node": {
"title": "Brown"
}
},
{
"node": {
"title": "Yellow"
}
},
{
"node": {
"title": "Red"
}
},
{
"node": {
"title": "Purple"
}
},
{
"node": {
"title": "Blue"
}
}
]
}
}
}
}
}
]
}
}
},
}
Switch from page-based to cursor-based pagination
In REST, you paginate results using query parameters. For example, using the limit
parameter, you can split the total entries into manageable groups and paginate through them with page
:
/admin/api/2021-01/products.json?limit=1&page=2
In GraphQL, you can apply similar concepts with cursor-based pagination. When using connections, you need to provide the first
or last
argument, which specifies the number of items you want returned. This is equivalent to the limit
parameter in REST.
Requesting cursor
and pageInfo
fields
Requesting the cursor
field in GraphQL lets you get a reference to the position of a node within the connections. You can use that reference to obtain the next set of items. The cursor
field in GraphQL is equivalent to page
in REST, but with more precision, robustness, and flexibility.
You can also request the pageInfo
field in GraphQL to determine if there are any more pages to request. The nested fields hasNextPage
and hasPreviousPage
are boolean fields which let you know if you can paginate forwards or backwards. If both are false
, then there are no more results or pages.
Tie it all together
The following example query explores the connection between a shop and its orders and shows how to retrieve specific fields. The query requests the first three elements on the orders connection. On each of the orders, the id
, name
, and email
fields are requested. The cursor
and pageInfo
fields are also included in the query to handle pagination.
GraphQL query
POST /admin/api/2021-01/graphql.json
{
orders(first: 3) {
edges {
cursor
node {
id
name
email
}
}
pageInfo {
hasNextPage
}
}
}
JSON response
{
"data": {
"orders": {
"edges": [
{
"cursor": "eyJsYXN0X2lkIjo0MTA0Nzk0OTMxNzYsImxhc3RfdmFsdWUiOiIyMDE3LTA0LTMwIDA0OjQ2OjA3In0=",
"node": {
"id": "gid://shopify/Order/410479493176",
"name": "#1592",
"email": "Maya.Schinner@developer-tools.shopifyapps.com"
}
},
{
"cursor": "eyJsYXN0X2lkIjo0MTA0Nzg1NDI5MDQsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTAyIDA4OjU5OjM4In0=",
"node": {
"id": "gid://shopify/Order/410478542904",
"name": "#1564",
"email": "Verda.Fritsch@developer-tools.shopifyapps.com"
}
},
{
"cursor": "eyJsYXN0X2lkIjo0MTA0ODA5Njc3MzYsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTAyIDE1OjQyOjQ3In0=",
"node": {
"id": "gid://shopify/Order/410480967736",
"name": "#1633",
"email": "Gino.Davis@developer-tools.shopifyapps.com"
}
}],
"pageInfo": {
"hasNextPage": true
}
}
}
}
}
}
The query returns the data for the first three orders. To get the next three orders, you need to add the cursor
value to the connection arguments.
The data returned indicates that each node
has a cursor
. Using the last cursor
that was returned allows you to continue from that point. You can continue paginating using the same method until hasNextPage
returns false
.
GraphQL query
POST /admin/api/2021-01/graphql.json
{
orders(first: 3, after: "eyJsYXN0X2lkIjo0MTA0ODA5Njc3MzYsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTAyIDE1OjQyOjQ3In0=") {
edges {
cursor
node {
id
name
email
}
}
pageInfo {
hasNextPage
}
}
}
JSON response
{
"data": {
"orders": {
"edges": [
{
"cursor": "eyJsYXN0X2lkIjo0MTA0ODU1ODgwMjQsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTA0IDAxOjI3OjAxIn0=",
"node": {
"id": "gid://shopify/Order/410485588024",
"name": "#1768",
"email": "Gino.Davis@developer-tools.shopifyapps.com"
}
},
{
"cursor": "eyJsYXN0X2lkIjo0MTA0ODY3Njc2NzIsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTA0IDEzOjQ1OjM2In0=",
"node": {
"id": "gid://shopify/Order/410486767672",
"name": "#1799",
"email": "Chanel.Auer@developer-tools.shopifyapps.com"
}
},
{
"cursor": "eyJsYXN0X2lkIjo0MTA0Nzg4Mzc4MTYsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTA0IDIwOjU1OjQ5In0=",
"node": {
"id": "gid://shopify/Order/410478837816",
"name": "#1573",
"email": "Harmon.Bernhard@developer-tools.shopifyapps.com"
}
}],
"pageInfo": {
"hasNextPage": true
}
}
}
}