Skip to main content

Migrate to GraphQL from REST

Legacy

The REST Admin API is a legacy API as of October 1, 2024. All apps and integrations should be built with the GraphQL Admin API. Read on to learn how to migrate from the REST Admin API to the GraphQL Admin API.

When you begin using GraphQL, you need to change how you think about retrieving and working with data. This guide describes some of the key considerations when migrating from REST to GraphQL. It's meant for app developers who are primarily using REST in their apps.


Anchor to Finding GraphQL objects and operationsFinding GraphQL objects and operations

Many REST Admin API resources and endpoints have a direct equivalent in GraphQL. Most GET, PUT, POST and DELETE requests in REST have an equivalent query or mutation in GraphQL.

However, some REST resources and operations might not have an exact GraphQL equivalent. This is because Shopify GraphQL APIs are built to leverage the efficiencies of GraphQL, newer platform features are exposed only in GraphQL, and some enhancements to the Shopify data model are reflected in only the GraphQL schema. The functionality included in one REST POST, PUT, or DELETE operation is often split between multiple GraphQL mutations.

You can explore the GraphQL Admin API reference and build your mutations using the GraphiQL explorer to identify which mutation is appropriate for your use case.

Anchor to Example: Direct equivalent in GraphQLExample: Direct equivalent in GraphQL

Retrieving information about a single customer is very similar in REST and GraphQL.

In this example, the GraphQL query retrieves all of the same information as is returned in the REST GET request. However, some of the field names and data structures are slightly different from their REST equivalents.

Tip

In GraphQL, you can optimize your query to retrieve only the fields that you need.

REST

Endpoint

GET https://{shop}.myshopify.com/admin/api/{api_version}/customer/6201722765389.json

JSON response

HTTP/1.1 200 OK
{
"customer": {
"id": 6201722765389,
"email": "russel.winfield@example.com",
"accepts_marketing": false,
"created_at": "2023-10-31T16:41:34-04:00",
"updated_at": "2023-11-15T17:57:44-04:00",
"first_name": "Russell",
"last_name": "Winfield",
"orders_count": 3,
"state": "disabled",
"total_spent": "6366.19",
"last_order_id": 4649035038797,
"note": "Customer note",
"verified_email": true,
"multipass_identifier": null,
"tax_exempt": false,
"tags": "VIP",
"last_order_name": "#1003",
"currency": "CAD",
"phone": "+16135550135",
"addresses": [
{
"id": 7647998738509,
"customer_id": 6201722765389,
"first_name": "Russell",
"last_name": "Winfield",
"company": "Company Name",
"address1": "105 Victoria St",
"address2": "",
"city": "Toronto",
"province": "Ontario",
"country": "Canada",
"zip": "M5C1N7",
"phone": "+16135550135",
"name": "Russell Winfield",
"province_code": "ON",
"country_code": "CA",
"country_name": "Canada",
"default": true
}
],
"accepts_marketing_updated_at": null,
"marketing_opt_in_level": null,
"tax_exemptions": [],
"email_marketing_consent": {
"state": "not_subscribed",
"opt_in_level": null,
"consent_updated_at": null
},
"sms_marketing_consent": {
"state": "not_subscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": null,
"consent_collected_from": "OTHER"
},
"admin_graphql_api_id": "gid://shopify/Customer/6201722765389",
"default_address": {
"id": 7647998738509,
"customer_id": 6201722765389,
"first_name": "Russell",
"last_name": "Winfield",
"company": "Company Name",
"address1": "105 Victoria St",
"address2": "",
"city": "Toronto",
"province": "Ontario",
"country": "Canada",
"zip": "M5C1N7",
"phone": "+16135550135",
"name": "Russell Winfield",
"province_code": "ON",
"country_code": "CA",
"country_name": "Canada",
"default": true
}
}
}

GraphQL

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

Graphql query

{
customer(id: "gid://shopify/Customer/6201722765389") {
id
email
emailMarketingConsent {
marketingState
consentUpdatedAt
marketingOptInLevel
}
createdAt
updatedAt
firstName
lastName
numberOfOrders
state
amountSpent {
amount
currencyCode
}
lastOrder {
id
name
}
note
verifiedEmail
multipassIdentifier
taxExempt
tags
market {
currencySettings {
baseCurrency {
currencyCode
}
}
}
phone
addresses {
id
firstName
lastName
company
address1
address2
city
province
zip
phone
name
provinceCode
countryCodeV2
country
}
taxExemptions
smsMarketingConsent {
marketingState
marketingOptInLevel
consentUpdatedAt
consentCollectedFrom
}
defaultAddress {
id
firstName
lastName
company
address1
address2
city
province
country
zip
phone
name
provinceCode
countryCodeV2
country
}
}
}

JSON response

{
"data": {
"customer": {
"id": "gid://shopify/Customer/6201722765389",
"email": "russel.winfield@example.com",
"emailMarketingConsent": {
"marketingState": "NOT_SUBSCRIBED",
"consentUpdatedAt": null,
"marketingOptInLevel": "SINGLE_OPT_IN"
},
"createdAt": "2023-10-31T16:41:34Z",
"updatedAt": "2023-11-15T17:57:44Z",
"firstName": "Russell",
"lastName": "Winfield",
"numberOfOrders": "3",
"state": "DISABLED",
"amountSpent": {
"amount": "6366.19",
"currencyCode": "CAD"
},
"lastOrder": {
"id": "gid://shopify/Order/4649035038797",
"name": "#1003"
},
"note": "Customer note",
"verifiedEmail": true,
"multipassIdentifier": null,
"taxExempt": false,
"tags": [
"VIP"
],
"market": {
"currencySettings": {
"baseCurrency": {
"currencyCode": "CAD"
}
}
},
"phone": "+16135550135",
"addresses": [
{
"id": "gid://shopify/MailingAddress/7647998738509?model_name=CustomerAddress",
"firstName": "Russell",
"lastName": "Winfield",
"company": "Company Name",
"address1": "105 Victoria St",
"address2": null,
"city": "Toronto",
"provinceCode": null,
"zip": "M5C1N7",
"phone": null,
"name": "Russell Winfield",
"countryCodeV2": "CA",
"country": "Canada"
},
{
"id": "gid://shopify/MailingAddress/7676436873293?model_name=CustomerAddress",
"firstName": "Russell",
"lastName": "Winfield",
"company": "Company Name",
"address1": "105 Victoria St",
"address2": null,
"city": "Toronto",
"provinceCode": "ON",
"zip": "M5C 1N7",
"phone": null,
"name": "Russell Winfield",
"countryCodeV2": "CA",
"country": "Canada"
}
],
"taxExemptions": [],
"smsMarketingConsent": {
"marketingState": "NOT_SUBSCRIBED",
"marketingOptInLevel": "UNKNOWN",
"consentUpdatedAt": null,
"consentCollectedFrom": "OTHER"
},
"defaultAddress": {
"id": "gid://shopify/MailingAddress/7676436873293?model_name=CustomerAddress",
"firstName": "Russell",
"lastName": "Winfield",
"company": "Company Name",
"address1": "105 Victoria St",
"address2": null,
"city": "Toronto",
"province": "Ontario",
"country": "Canada",
"zip": "M5C 1N7",
"phone": null,
"name": "Russell Winfield",
"provinceCode": "ON",
"countryCodeV2": "CA"
}
}
}
}

Anchor to Example: REST functionality split in GraphQLExample: REST functionality split in GraphQL

In the following example, using REST you can use the generic product update request to delete an image from a product. This functionality isn't included in the GraphQL Admin API’s productUpdate mutation. Instead, to delete images, you need to use the productDeleteMedia mutation.

REST

Endpoint

PUT https://{shop}.myshopify.com/admin/api/{api_version}/products/{product_id}.json

Request body

{
"product": {
"id":632910392,
"images": [
]
}
}

JSON response (truncated)

{
"product": {
"id": 632910392,
"title": "IPod Nano - 8GB",
...
"images": [],
"image": null
}
}

GraphQL

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL mutation

mutation productDeleteMedia {
productDeleteMedia(mediaIds:["gid://shopify/MediaImage/183532652","gid://shopify/MediaImage/731367280"],productId:"gid://shopify/Product/632910392") {
deletedMediaIds
product {
id
title
media(first: 5) {
nodes {
alt
mediaContentType
status
}
}
}
}
}

JSON response

{
"data": {
"productDeleteMedia": {
"deletedMediaIds": [
"gid://shopify/MediaImage/183532652",
"gid://shopify/MediaImage/731367280"
],
"product": {
"id": "gid://shopify/Product/632910392",
"title": "IPod Nano - 8GB",
"media": {
"nodes": []
}
}
}
}
}

Anchor to Finding required access scopesFinding required access scopes

After you've found an equivalent GraphQL object, you'll need to determine if your app has the appropriate access scopes to work with the object and its associated queries and mutations. Access scopes are defined at the top of the page for each type in the GraphQL Admin API reference.

You can check your app’s granted access scopes using the appInstallation query in the GraphQL Admin API.


Anchor to Translating IDs from REST to GraphQLTranslating IDs from REST to GraphQL

REST and GraphQL use different formats for resource and object IDs. If you use stored IDs to retrieve resources in the REST Admin API, then you need to retrieve and store the equivalent GraphQL API global ID (GID) before you can run the corresponding queries and mutations in GraphQL APIs.

Most REST responses include the admin_graphql_api_id property. You can use the ID in this property to directly query the equivalent object in GraphQL.

You can also view some common examples of GraphQL GIDs in our GID documentation.

Note

The admin_graphql_api_id property is returned only to apps. It isn't included in responses that are viewed in a web browser.

Anchor to Example: Retrieve a GraphQL object ID from RESTExample: Retrieve a GraphQL object ID from REST

The following example retrieves a product variant ID in REST, and then uses the admin_graphql_api_id property to query the equivalent product variant ID in GraphQL.

REST

Endpoint

GET https://{shop}.myshopify.com/admin/api/{api_version}/products/{product_id}/variants/{variant_id}.json

JSON response (truncated)

{
"variant": {
"id": 12195007594552,
"product_id": 1321540747320,
"title": "Default Title",
"...": "...",
"admin_graphql_api_id": "gid://shopify/ProductVariant/12195007594552"
}
}

GraphQL

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

{
productVariant (id: "gid://shopify/ProductVariant/12195007594552") {
id
title
}
}

JSON response

{
"data": {
"productVariant": {
"id": "gid://shopify/ProductVariant/12195007594552",
"title": "Default Title"
}
},
}

Anchor to Resources that don't return an Admin GraphQL API IDResources that don't return an Admin GraphQL API ID

In some cases, a resource might not return an admin_graphql_api_id. This ID might not be available for several reasons. For example, a resource might not have an exact equivalent GraphQL object. In this case, you can determine the appropriate object to use, and then use examples in the GraphQL Admin API reference or query the object in the GraphiQL explorer to learn about the ID format.

Anchor to Anatomy of a GraphQL IDAnatomy of a GraphQL ID

GraphQL ID generation is implementation dependent, and doesn't follow any convention other than being a URI. There is no guarantee that GraphQL IDs will follow the structure gid://shopify/{resource}/{rest_id} as in the previous example, so you shouldn't generate IDs programmatically. Always treat the admin_graphql_api_id string as an opaque ID.

The following example shows how the admin_graphql_api_id property doesn't always follow an expected structure:

Example

Endpoint

GET /admin/api/{api_version}/inventory_levels.json?inventory_item_ids={inventory_item_id}

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"
}
]
}

Anchor to Implementing and updating paginationImplementing and updating pagination

In REST, you can paginate data using query parameters. Using the limit parameter, you can split the total number of entries into manageable groups:

https://{shop}.myshopify.com/admin/api/{api_version}/products.json?limit=1

To get the next page of results, you can make a request to the URL stored in the Link header of the last response:

https://{shop}.myshopify.com/admin/api/{api_version}/products.json?page_info={page_info_id}&limit=3

In GraphQL, you can apply similar concepts using cursor-based pagination. When querying a connection in GraphQL, you need to provide the first or last argument, which specifies the number of items that you want returned from the beginning or the end of the set. This is equivalent to the limit parameter in REST.

Tip

A connection is a GraphQL type that's used to retrieve a list of nodes. A node is an individual item or data object in the list, and represents the data that you're querying for. For example, the ProductConnection finds all the edges that connect the query root to a Product node.

You can query the cursor field to get a reference to the position of a node in a connection, and use that reference to obtain the next set of items. The cursor field in GraphQL is equivalent to the page_info parameter in REST.

You can also query the pageInfo object in GraphQL to determine if there are any more pages to request. The fields hasNextPage and hasPreviousPage are boolean fields that indicate whether you can paginate forwards or backwards. Depending on the direction that you're paging in, you can use the relevant field to determine when you've reached the end of the list. For example, if you're paginating forward using the first argument, then you've reached the end of the list when hasNextPage is false.

You can retrieve up to 250 resources in a single request. If you need to retrieve larger volumes of data, then you can perform a bulk query operation. A bulk query operation lets you asynchronously fetch all of the items in a list using as little as one query, avoiding the need to paginate results and the risk of your requests being throttled.

The following example shows how to query the first three products and determine whether you can paginate forward. The JSON response returns the product IDs, titles, and cursor IDs, and indicates that you can paginate forward.

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

{
products(first: 3) {
edges {
cursor
node {
id
title
handle
}
}
pageInfo {
hasNextPage
}
}
}

JSON response

{
"products": {
"edges": [
{
"node": {
"id": "gid://shopify/Product/108828309",
"title": "Draft",
"handle": "draft"
},
"cursor": "eyJsYXN0X2lkIjoxMDg4MjgzMDksImxhc3RfdmFsdWUiOiIxMDg4MjgzMDkifQ=="
},
{
"node": {
"id": "gid://shopify/Product/121709582",
"title": "Boots",
"handle": "boots"
},
"cursor": "eyJsYXN0X2lkIjoxMjE3MDk1ODIsImxhc3RfdmFsdWUiOiIxMjE3MDk1ODIifQ=="
},
{
"node": {
"id": "gid://shopify/Product/440089423",
"title": "IPod Nano - 8GB",
"handle": "ipod-nano"
},
"cursor": "eyJsYXN0X2lkIjo0NDAwODk0MjMsImxhc3RfdmFsdWUiOiI0NDAwODk0MjMifQ=="
}
],
"pageInfo": {
"hasNextPage": true
}
}
}

You can provide the after argument with the last cursor from the response to request the next set of data:

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

{
products(first: 3, after: "eyJsYXN0X2lkIjo0NDAwODk0MjMsImxhc3RfdmFsdWUiOiI0NDAwODk0MjMifQ==") {
edges {
cursor
node {
id
title
}
}
pageInfo {
hasNextPage
}
}
}

JSON response

{
"data": {
"products": {
"edges": [
{
"cursor": "eyJsYXN0X2lkIjoyMDI0MzMyMTk3OTEwLCJsYXN0X3ZhbHVlIjoiMjAyNDMzMjE5NzkxMCJ9",
"node": {
"id": "gid://shopify/Product/2024332197910",
"title": "Bronze guy",
"handle": "bronze-guy"
}
},
{
"cursor": "eyJsYXN0X2lkIjoyMDI0MzMyMjMwNjc4LCJsYXN0X3ZhbHVlIjoiMjAyNDMzMjIzMDY3OCJ9",
"node": {
"id": "gid://shopify/Product/2024332230678",
"title": "Dalmatian",
"handle": "dalmatian"
}
},
{
"cursor": "eyJsYXN0X2lkIjoyMDI0MzMyMjk2MjE0LCJsYXN0X3ZhbHVlIjoiMjAyNDMzMjI5NjIxNCJ9",
"node": {
"id": "gid://shopify/Product/2024332296214",
"title": "Cowlick pigeon",
"handle": "cowlick-pigeon"
}
}
],
"pageInfo": {
"hasNextPage": true
}
}
}
}

Anchor to Understanding error handlingUnderstanding error handling

In GraphQL, you can't always rely on HTTP status codes to determine whether a query or mutation completed without errors. In cases that would typically produce 4xx or 5xx errors in REST, GraphQL might return a 200 OK status with additional error information in its response.

The 4xx and 5xx errors occur infrequently. They're often related to network communications, your account, or an issue with Shopify’s services.

To learn about the possible response codes and what they mean, refer to Shopify API response status and error codes.

If a query results in an error, then an errors object is returned. The object contains additional detail that can help you debug your operation.

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

{
shop {
name
address
}
}

JSON response - 200 OK

{
"errors": [
{
"message": "Field 'address' doesn't exist on type 'Shop'",
"locations": [
{
"line": 4,
"column": 5
}
],
"path": [
"query",
"shop",
"address"
],
"extensions": {
"code": "undefinedField",
"typeName": "Shop",
"fieldName": "address"
}
}
]
}

The response for mutations also can return additional detail to help debug your mutation. To access this information, you must request the userErrors field.

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL mutation

mutation {
productDelete(input: {id: "gid://shopify/Product/123"}) {
deletedProductId
userErrors {
field
message
}
}
}

JSON response - 200 OK

{
"data": {
"productDelete": {
"deletedProductId": null,
"userErrors": [
{
"field": [
"id"
],
"message": "Product does not exist"
}
]
}
}
}

Anchor to Updating your error handlingUpdating your error handling

The following is a basic JavaScript example of how you might handle an error in REST, and how your error handling might be updated to parse errors returned in GraphQL responses with a status of 200 OK.

Handling errors in REST

const response = await fetch(url);

if (!response.ok) {
throw new Error(`HTTP ${response.status} - ${response.statusText}`);
}

Handling errors in GraphQL

const data = await response.json();

if (data.errors) {
console.error('GraphQL Error: ', data.errors);
throw new Error('GraphQL Error');
}

Anchor to Understanding rate limitingUnderstanding rate limiting

To ensure the Shopify platform remains stable and fair for everyone, both the REST and GraphQL Admin APIs are rate-limited. However, these two APIs use a different rate limiting approach.

The REST Admin API is governed by request-based limits. It provides credits that clients spend every time they make a request, and those credits are refilled every second. This allows clients to keep a request pace that never limits the API usage (two requests for each second) and makes occasional request bursts when needed (making 10 requests for each second).

However, the request-based model has the following limitations:

  • Clients use the same amount of credits regardless, even if they don’t need all the data in an API response.
  • POST, PUT, PATCH and DELETE requests produce side effects that demand more load on servers than GET requests, which only reads existing data. Despite the difference in resource usage, all these requests consume the same amount of credits in the request-based model.

GraphQL addresses some limitations of common methods that are typically used in REST APIs.

The GraphQL Admin API uses a calculated cost query method for rate limiting. By default, in a calculated query cost method, clients receive 50 points per second up to a limit of 1,000 points.

The main difference from the REST request-based model is that every GraphQL request has a different cost.

The best way to determine the true cost of a query or mutation is to run it. In the following mutation example that creates a metaobject, the JSON response includes information about the total mutation cost and the client’s current quota under the extensions field. You can include an Shopify-GraphQL-Cost-Debug=1 header to receive a more detailed breakdown of the query or mutation cost.

You can use the currentlyAvailable and restoreRate fields returned in the throttleStatus object to understand how many points you have available and how quickly they'll refill. This also helps you to adjust if a client has a different bucket size or restore rate, or if the global allocation changes in future.

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL mutation

mutation {
metaobjectCreate(metaobject: {
type: "$app:product_highlight",
fields: [
{
key: "title",
value: "100% Reusable Plastics"
},
{
key: "description",
value: "Rest easy - our glasses are made from 100% reusable materials"
},
{
key: "creative",
value: "gid://shopify/MediaImage/1"
}
]
}) {
metaobject {
id
type
handle
title: field(key: "title") { value }
description: field(key: "description") { value },
creative: field(key: "creative") { value }
}
}
}

JSON response

{
"data": {
"metaobjectCreate": {
"metaobject": {
"id": "gid://shopify/Metaobject/15328903245",
"type": "app--2315872--product_highlight",
"handle": "app--2315872--product-highlight-ueyger14",
"title": {
"value": "100% Reusable Plastics"
},
"description": {
"value": "Rest easy - our glasses are made from 100% reusable materials"
},
"creative": {
"value": "gid:\/\/shopify\/MediaImage\/1"
}
}
}
},
"extensions": {
"cost": {
"requestedQueryCost": 13,
"actualQueryCost": 13,
"throttleStatus": {
"maximumAvailable": 1000,
"currentlyAvailable": 987,
"restoreRate": 50
}
}
}
}

In REST, if an app reaches an API rate limit, then it receives a 429 Too Many Requests response and a message that a throttle has been applied.

In GraphQL, if an app reaches an API rate limit, then it receives a 200 OK response, but the response body contains an error with a message that a throttle has been applied. You can handle for these throttled requests the same way that you handle for other errors returned by GraphQL.

Example response

{
"errors": [
{
"message": "Throttled",
"extensions": {
"code": "THROTTLED",
"documentation": "https://shopify.dev/api/usage/limits#rate-limits"
}
}
],
"extensions": {
"cost": {
"requestedQueryCost": 202,
"actualQueryCost": null,
"throttleStatus": {
"maximumAvailable": 1000,
"currentlyAvailable": 186,
"restoreRate": 50
}
}
}
}

Anchor to Optimizing your requestsOptimizing your requests

Depending on your use case, you might be able to further optimize your GraphQL requests. Optimizing requests improves the performance of your app by reducing the number of calls that need to be made to the GraphQL endpoint, and helps you to avoid being throttled by sending fewer queries or queries with a lower cost.

You can investigate the following optimization opportunities:

Anchor to Combine multiple REST requests into a single GraphQL queryCombine 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, in the case of an order, you might want to know the total price, 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:

  • /orders/{order_id}.json
  • /products/{product_id}/variants.json
  • /customers/{customer_id}/metafields.json

Using GraphQL, you can make a single request using connections to get the desired data:

Example

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

query {
order(id:"gid://shopify/Order/5369369690390") {
id
totalPriceSet{
shopMoney {
amount
}
}
customer {
displayName
metafields (first:10) {
edges {
node {
key
value
}
}
}
}
lineItems (first:10) {
edges {
node {
variant {
product {
variants(first:10) {
edges {
node {
title
}
}
}
}
}
}
}
}
}
}

JSON response

{
"data": {
"order": {
"id": "gid://shopify/Order/5369369690390",
"totalPriceSet": {
"shopMoney": {
"amount": "2099.85"
}
},
"customer": {
"displayName": "Ayumu Hirano",
"metafields": {
"edges": [
{
"node": {
"key": "birth_date",
"value": "1990-02-22"
}
}
]
}
},
"lineItems": {
"edges": [
{
"node": {
"variant": {
"product": {
"variants": {
"edges": [
{
"node": {
"title": "Ice"
}
},
{
"node": {
"title": "Dawn"
}
}
]
}
}
}
}
}
]
}
}
}
}

Anchor to Request only the data you needRequest only the data you need

To avoid over-fetching, request only the fields that your app is using in your queries and mutations. This reduces the payload size, reducing bandwidth usage and improving performance.

For example, when you request a customer using REST, the response contains over 60 fields:

REST

Endpoint

GET https://{shop}.myshopify.com/admin/api/{api_version}/customers/{customer_id}.json

JSON response

{
"customer": {
"id": 1073339469,
"email": "bob@example.com",
"accepts_marketing": false,
"created_at": "{api_version}-03T13:37:56-04:00",
"updated_at": "{api_version}-03T13:37:56-04:00",
"first_name": "Bob",
"last_name": "Bobsen",
"orders_count": 1,
"state": "disabled",
"total_spent": "199.65",
"last_order_id": 450789469,
"note": null,
"verified_email": true,
"multipass_identifier": null,
"tax_exempt": false,
"tags": "Léon, Noël",
"last_order_name": "#1001",
"currency": "USD",
"phone": "+16136120707",
"addresses": [
{
"id": 207119551,
"customer_id": 207119551,
"first_name": null,
"last_name": null,
"company": null,
"address1": "123 Amoebobacterieae St",
"address2": "",
"city": "Ottawa",
"province": "Ontario",
"country": "United States",
"zip": "40202",
"phone": "555-625-1199",
"name": "",
"province_code": "KY",
"country_code": "US",
"country_name": "United States",
"default": true
}
],
"accepts_marketing_updated_at": "2005-06-12T11:57:11-04:00",
"marketing_opt_in_level": null,
"tax_exemptions": [],
"email_marketing_consent": {
"state": "not_subscribed",
"opt_in_level": null,
"consent_updated_at": "2004-06-13T11:57:11-04:00"
},
"sms_marketing_consent": {
"state": "not_subscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "{api_version}-03T13:37:56-04:00",
"consent_collected_from": "OTHER"
},
"admin_graphql_api_id": "gid://shopify/Customer/207119551",
"default_address": {
"id": 207119551,
"customer_id": 207119551,
"first_name": null,
"last_name": null,
"company": null,
"address1": "Chestnut Street 92",
"address2": "",
"city": "Louisville",
"province": "Kentucky",
"country": "United States",
"zip": "40202",
"phone": "555-625-1199",
"name": "",
"province_code": "KY",
"country_code": "US",
"country_name": "United States",
"default": true
}
}
}

If you only need to retrieve customer contact and address information, then you can build your query to retrieve only this information:

GraphQL

Endpoint

POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json

GraphQL query

{
customer(id: "gid://shopify/Customer/544365967") {
email
firstName
lastName
defaultAddress {
address1
city
province
zip
country
}
}
}

JSON response

{
"customer": {
"email": "bob@example.com",
"firstName": "Bob",
"lastName": "Bobsen",
"defaultAddress": {
"address1": "123 Amoebobacterieae St",
"city": "Ottawa",
"province": "Ontario",
"zip": "K2P0V6",
"country": "Canada"
}
}
}

Anchor to Use the best mutationUse the best mutation

In some cases, you can lessen the number of calls that you need to make by choosing a mutation that's tailored for your use case.

For example, if you wanted to make a copy of a product using a new name, then you could query for the product to be duplicated using a product query, edit the product fields, and then create a new product using a productCreate mutation. However, the GraphQL Admin API provides a productDuplicate mutation, which allows you to duplicate the product and update key information including the title and status, avoiding a round trip and lessening the amount of data that needs to be sent to and retrieved from Shopify.

If you don't need to use the duplicated product right away, then you also have the option to use productDuplicateAsyncV2 to duplicate the product asynchronously.

If you need to import or export large volumes of data, then you can perform a bulk operation. Bulk operations lessen complexity by reducing the number of requests that you need to make, avoiding throttling and considerations such as pagination.

For example, if you retrieved all products in REST from the /products.json endpoint, then you can instead use bulkOperationRunQuery.

Learn more about bulk queries and mutations.


Anchor to Understanding library supportUnderstanding library support

Many API client libraries that you might use to call the Shopify Admin API support both REST and GraphQL.

  • Official Shopify API libraries:

  • Fetch API / Node Fetch

  • Axios

  • Some third-party Admin API libraries

    However, you need to make updates to your code that uses these libraries. You might have to perform some or all of the following tasks:

  • Switching from a REST client to a GraphQL client

  • Changing the API endpoint

  • Updating the request type

  • Adding your query or mutation and specifying the fields to return

  • Destructuring or digging into the response

    For examples of updating your implementation using some commonly used libraries, refer to Updating API calls in your app.


Learn how to use GraphQL APIs with our .dev Assistant, Shopify Dev Assistant Visual Studio Code extension, and how-to guides that cover different use cases:


Was this page helpful?