# storefront

Executes queries against Shopify's Storefront GraphQL API. Works the same way as the `graphql` tag, but targets the Storefront API instead of the Admin API. The response is stored in a variable for further processing.

The Storefront API provides access to public store data — products, collections, cart operations, and more — without requiring admin-level permissions.

```liquid
{% storefront query: my_query, variables: my_variables as result %}

{% if result.product %}
  {% log result.product.title %}
{% endif %}
```

#### Syntax

```liquid
{% storefront query: query_string, variables: variables_object as result_variable %}
```

#### Parameters

| Parameter   | Required | Description                                           |
| ----------- | -------- | ----------------------------------------------------- |
| `query`     | Yes      | Storefront GraphQL query string                       |
| `variables` | No       | Object containing variables for the GraphQL operation |

#### Result Object

The result contains the returned data directly accessible at the top level. Query fields are available as properties on the result variable.

If errors occur, they are available in the `errors` property:

| Property | Type       | Description                                    |
| -------- | ---------- | ---------------------------------------------- |
| `errors` | array/null | Array of error objects if the operation failed |

#### Examples

**Fetch a product by handle:**

```liquid
{% capture query %}
  query getProduct($handle: String!) {
    productByHandle(handle: $handle) {
      id
      title
      description
      priceRange {
        minVariantPrice {
          amount
          currencyCode
        }
      }
    }
  }
{% endcapture %}

{% json variables %}
  {
    "handle": "classic-tee"
  }
{% endjson %}

{% storefront query: query, variables: variables as result %}

{% if result.productByHandle %}
  {% log result.productByHandle.title %}
  {% log result.productByHandle.priceRange.minVariantPrice.amount %}
{% endif %}
```

**List products from a collection:**

```liquid
{% capture query %}
  query getCollection($handle: String!, $first: Int!) {
    collectionByHandle(handle: $handle) {
      title
      products(first: $first) {
        edges {
          node {
            id
            title
            handle
            availableForSale
          }
        }
      }
    }
  }
{% endcapture %}

{% json variables %}
  {
    "handle": "summer-sale",
    "first": 50
  }
{% endjson %}

{% storefront query: query, variables: variables as result %}

{% for edge in result.collectionByHandle.products.edges %}
  {% log edge.node.title | append: " - Available: " | append: edge.node.availableForSale %}
{% endfor %}
```

**Fetch product recommendations:**

```liquid
{% capture query %}
  query getRecommendations($productId: ID!) {
    productRecommendations(productId: $productId) {
      id
      title
      handle
      priceRange {
        minVariantPrice {
          amount
          currencyCode
        }
      }
    }
  }
{% endcapture %}

{% json variables %}
  {
    "productId": "gid://shopify/Product/{{ product_id }}"
  }
{% endjson %}

{% storefront query: query, variables: variables as result %}

{% for product in result.productRecommendations %}
  {% log product.title | append: " ($" | append: product.priceRange.minVariantPrice.amount | append: ")" %}
{% endfor %}
```

**Query with pagination:**

```liquid
{% capture query %}
  query getProducts($cursor: String) {
    products(first: 50, after: $cursor) {
      edges {
        node {
          id
          title
          availableForSale
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
{% endcapture %}

{% assign cursor = null %}
{% assign all_products = "" | split: "" %}

{% for i in (1..100) %}
  {% json variables %}
    {
      "cursor": {{ cursor | json }}
    }
  {% endjson %}

  {% storefront query: query, variables: variables as result %}

  {% for edge in result.products.edges %}
    {% assign all_products = all_products | push: edge.node %}
  {% endfor %}

  {% if result.products.pageInfo.hasNextPage %}
    {% assign cursor = result.products.pageInfo.endCursor %}
  {% else %}
    {% break %}
  {% endif %}
{% endfor %}

{% log "Total products: " | append: all_products.size %}
```

**Handle errors:**

```liquid
{% storefront query: query, variables: variables as result %}

{% if result.errors %}
  {% log "Storefront API error:" %}
  {% for error in result.errors %}
    {% log error.message %}
  {% endfor %}
{% else %}
  {% log result %}
{% endif %}
```

#### Storefront vs Admin API

| Feature   | `storefront`                               | `graphql`                                    |
| --------- | ------------------------------------------ | -------------------------------------------- |
| API       | Storefront GraphQL API                     | Admin GraphQL API                            |
| Access    | Public store data                          | Full admin access                            |
| Products  | Read-only (public fields)                  | Read/write (all fields)                      |
| Orders    | No access                                  | Full access                                  |
| Customers | No access                                  | Full access                                  |
| Cart      | Yes                                        | No                                           |
| Use case  | Public data, recommendations, availability | Data management, mutations, admin operations |

#### Notes

* Consumes 1 credit per operation
* Uses the store's API version configured in DataJet
* Storefront API errors are automatically logged to the script logs
* Variables must be a valid object (use `json` tag to construct complex variables)
* Result properties are accessed directly (e.g., `result.products`, not `result.data.products`)
* Refer to [Shopify's Storefront API documentation](https://shopify.dev/docs/api/storefront) for available queries
