Migrating to GraphQL from REST

Translating from REST IDs to GraphQL IDs

To help with migrating from our REST to the GraphQL, REST responses now include the GraphQL Admin API ID field, admin_graphql_api_id. You can use the ID in this field 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 it in GraphQL.

REST endpoint

GET /admin/api/2019-10/products/1321540747320/variants/12195007594552.json

REST response

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

GraphQL request

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

GraphQL Response

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

GraphQL IDs are opaque

GraphQL ID generation is implementation dependent, and as such does not follow any convention other than being a URI. There is no guarantee that GraphQL IDs will follow a similar "structure" (gid -> shopify -> resource -> rest_id) to the previous example, and so you shouldn't rely on building these IDs programatically.

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

REST endpoint

GET /admin/api/2019-10/inventory_levels.json?inventory_item_ids=12261979488312

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

Always treat the admin_graphql_api_id string as an opaque ID.

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 that object's Storefront API ID.

The following example shows how to request the storefrontId field on a product.

GraphQL request

query {
  product(id:"gid://shopify/Product/1324932956216") {
    id
    title
    storefrontId
  }
}

GraphQL response

{
  "data": {
    "product": {
      "id": "gid://shopify/Product/1324932956216",
      "title": "GQL Lego Connector",
      "storefrontId": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEzMjQ5MzI5NTYyMTY="
    }
  },
  ...
}

Nested Queries

Using connections, you can get information from 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, as well as the title of other variants belonging to the product in the order.

Using REST, we'd query the following endpoints and filter excess information.

  • /admin/api/2019-10/orders/{order_id}.json
  • /admin/api/2019-10/products/{product_id}/variants.json
  • /admin/api/2019-10/customers/{customer_id}/metafields.json

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

query {
  order(id:"gid://shopify/Order/410487128120") {
    id
    totalPrice
    customer {
      displayName
      metafields (first:10) {
        edges {
          node {
            description
          }
        }
      }
    }
    lineItems (first:10) {
      edges {
        node {
          variant {
            product {
              variants(first:10) {
                edges {
                  node {
                    title
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Pagination

In REST, you paginate via the query parameters. Using limit, you can split the total entries into manageable groups and paginate through them with page.

/admin/api/2019-10/products.json?limit=1&page=2

In GraphQL, you can apply similar concepts with cursor based pagination. When using connections, you'll want to provide the first or last argument, which specifies the number of items you want returned. This is equivalent to limit.

Requesting the cursor field 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. This is equivalent to page, but with more precision, robustness, and flexibility.

Finally, request the pagInfo field to determine if there are any more pages to request. The nested fields “hasNextPage” and “hasPreviousPage” are boolean fields which let us know if we'll be able to paginate forwards or backwards. If both are false, there are no more results or pages.

Tying it all together

This example query explores the connection between shops and orders. In our request, we’ll explore the connection between shops and orders, and how we can get only the fields we’re interested in.

This query asks for the first three elements on the orders connection. On each of these orders, the id, name, and email fields are requested. You request the cursor and pageInfo fields to handle pagination.

Request

query {
  orders(first: 3) {
    edges {
      cursor
      node {
        id
        name
        email
      }
    }
    pageInfo {
      hasNextPage
    }
  }
}

GraphQL 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. In order to get the next 3 orders, we’ll have to add the cursor value to our 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.

Request

query {
  orders(first: 3, after: "eyJsYXN0X2lkIjo0MTA0ODA5Njc3MzYsImxhc3RfdmFsdWUiOiIyMDE3LTA1LTAyIDE1OjQyOjQ3In0=") {
    edges {
      cursor
      node {
        id
        name
        email
      }
    }
    pageInfo {
      hasNextPage
    }
  }
}

GraphQL 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
    }
  }
}

Continue paginating using the same method until hasNextPage returns false.

Sign up for a Partner account to get started.

Sign up