Working with Product Lists

Plan: Composable Developer

Lesson 13 of 27 · 45 min

In this section, we’ll explore the details of querying and paginating lists of products.

This first example GraphQL query demonstrates how a list of a category’s products can be selected in the sub-field products.

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products {
edges {
node {
entityId
name
prices {
basePrice {
currencyCode
value
}
}
}
}
}
}
}
}
}
}

The GraphQL Storefront API will return exactly what you asked for:

{
"data": {
"site": {
"route": {
"node": {
"products": {
"edges": [
{
"node": {
"entityId": 112,
"name": "Product 1",
"prices": {
"basePrice": {
"currencyCode": "USD",
"value": 10.25
}
}
}
},
{
"node": {
"entityId": 115,
"name": "Product 2",
"prices": {
"basePrice": {
"currencyCode": "USD",
"value": 3
}
}
}
},
{
"node": {
"entityId": 116,
"name": "Product 3",
"prices": {
"basePrice": {
"currencyCode": "USD",
"value": 8.99
}
}
}
},

Each node in the products list shown above is of the GraphQL type Product. This same type is returned when querying site.category.products or the more generic site.products, as well as when using site.product or site.route to fetch a single product record rather than a list. Regardless of which query is your entry point, the sub-fields available for inspecting a product’s details are consistent.

See the GraphQL Storefront API documentation for full details on the sub-fields of Product.

Cursor List Pagination with Product Lists

Pagination is a way to traverse the relationship between sets of objects in a GraphQL query. In other words, it is a way to view more results than the default 10 objects. Let’s say you query for a basic product list and GraphQL returns a list of products, but you want to view more than the initial result. The product list that GraphQL fetched is a cursor list, which means that each object is assigned a unique cursor value that you can use to navigate the pages of results.

As a reminder, this strategy is an implementation of the Relay specification for GraphQL cursor connections.

Just as you can rely on the schema of the Product type regardless of the specific top-level query, the details we’ll cover here for paginating products apply in any product list context, whether you’re querying products within a category, using site.products, or using the more versatile site.search that we’ll examine in a later lesson.

The pageInfo value available on a collection, with its details startCursor, endCursor, hasNextPage and hasPreviousPage, is critical for determining what paging options to offer in your UI.

Example - Get Cursor Values and Page Information for a Product List

Let’s run a query for a product list and get each product’s cursor and the page information.

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

The results return all the product object cursors and details that you included in the query as node fields, as well as the page information at the bottom. The pageInfo tells you that you are looking at the first page of results and there is at least one more page. Let’s write a query to view the next page of results.

Using Filters to Navigate the Cursor List

The before and after filters are used as arguments in the query. They use cursor values to specify a starting point for the page of results. The example below uses the same query used in the previous section, but applies the after filter to input the end cursor and ask for the next page of results.

Get the Next Page of Results

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products(after: "YXJyYXljb25uZWN0aW9uOjk=") {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

To navigate back to a previous page of results, you would change the filter to before and input a startCursor like the example below:

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products(before: "YXJyYXljb25uZWN0aW9uOjEw") {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

Using First and Last Filters

The first and last filters allow you to query for x amount of objects before or after a certain cursor value. The default response is 10 objects, but you can use the first and last filters to ask for more or less than 10 objects depending on what you need. For example, take the previous query and say you only want to view the four products before the first object on the page.

If you are paginating backward, you will use the last filter and the startCursor value from the example query above.

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products(last: 4, before: "YXJyYXljb25uZWN0aW9uOjEw") {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

The results will look similar to other example results in this lesson, but GraphQL will only return four objects.

Paginating forward works the same way, except you will use the first and after filters to specify the starting point and amount of results you want returned. For example, let’s take the first example query in this section that is only asking for a list of product objects:

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

Add the first and after filters to the query to ask for the first five objects after the endCursor.

query CategoryProducts($path: String!) {
site {
route(path: $path) {
node {
... on Category {
products(first: 5, after: "YXJyYXljb25uZWN0aW9uOjE0") {
edges {
cursor
node {
entityId
name
sku
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
}
}

CollectionInfo

There is one more field that is useful for pagination purposes called collectionInfo. This field is useful when you are working with a large amount of product objects. It simply provides a number of totalItems on the list so you know if you are looking at the full list or just a portion of the list.

Example - collectionInfo on a query for the last three products before X

query CategoryProducts($path: String!) {
site{
route(path: $path) {
node {
... on Category {
products(last:3 before:"YXJyYXljb25uZWN0aW9uOjc="){
pageInfo{
startCursor
endCursor
}
collectionInfo{
totalItems
}
edges{
cursor
node{
entityId
}
}
}
}
}
}
}
}

The collectionInfo field is helpful in determining whether or not there are more results that cannot fit on one page of results. If the collectionInfo field is showing that there are 12 items, but you are only shown three, you know that more pagination is needed.

Specialized Product Lists

There are three special types of product lists that you can search for:

  • bestSellingProducts - returns a list of best selling products
  • newestProducts - returns a list of the newest products on your storefront
  • featuredProducts - returns a list of featured products

These specialized lists are worth mentioning because they are not available through other forms of filtering or sorting. The response for each of the queries below will be a list of products that fall into the specification of the query (best selling, newest products, or featured products).

Example Query - bestSellingProducts:

query{
site{
bestSellingProducts{
edges{
node{
entityId
sku
name
}
}
}
}
}

Resources