Feeding Data into AMP Pages with GraphQL

Last edit: Aug 16, 2019
  • Contributors:
  • diana-lakatos
  • ignacy
  • Slashek

This guide will help you feed data into your AMP pages using GraphQL. The guide explains the process through a real-life example of displaying products of an online store.

The guide explains the process through a real-life example of displaying products of an online store.

Requirements

This is an advanced tutorial. To follow it, you should be familiar with basic platformOS concepts, technologies, and the topics in the Get Started section, especially pages. You should also know what AMP pages are and how to create them.

Steps

Feeding data into AMP pages with GraphQL, in this case, displaying products on a page is a four-step process:

Step 1: Display list of products

Create the main page, HTML extended with AMP tags and objects app/views/pages/product-list.liquid:

---
slug: product-list
layout_name: application
---

{% content_for 'amp-dependencies' %}
  <script custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js" async></script>
  <script custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js" async></script>
  <script custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js" async></script>
{% endcontent_for %}

[...]
<amp-list src="api/products.json" layout="responsive">
  <template type="amp-mustache">
    <a href="/product-details/{% raw %}{{ slug }}{% endraw %}">
      <div>
        <amp-img src="{% raw %}{{ image }}{% endraw %}" width="300" layout="responsive" noloading></amp-img>
        <h2>{% raw %}{{ name }}{% endraw %}</h2>
        {% raw %}{{ description }}{% endraw %}
      </div>
      <div>${% raw %}{{ price }}{% endraw %}</div>
    </a>
  </template>
</amp-list>

Include the AMP dependencies in the layout file app/views/layouts/application.liquid:


{% yield 'amp-dependencies' %}

Notes

  • The data source for <amp-list> is products.json.
  • amp-mustache enables the 'mustache' (double curly braces) templating syntax.
  • The {% raw %} tag instructs the liquid preprocessor not to interpret the liquid tags and leaves them to AMP/mustache to process.

Step 2: Create JSON endpoint

Create the JSON endpoint, a dynamically generated JSON document from which AMP fetches the data that it displays app/views/pages/api/products.json.liquid:

---
slug: api/products
format: json
---

{% graphql g = 'get_products' %}
{% assign results = g.products.results %}

{
  "items": [
    {% for product in results %}
      {
        "name": {{ product.name | json }},
        "slug": {{ product.slug | json }},
        "description": {{ product.description | json }},
        "image": {{ product.photo.url | json }},
        "price": {{ product.price }}
      }{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ]
}

Notes

  • The GraphQL query is not given any argument, thus it retrieves all the products which are then sorted by price in ascending order with the sort: liquid filter.
  • Then the for loop populates the "items" list with JSON data that contains the info for each product.
  • The loop puts a comma after each element except for the last in the list.

Step 3: Fetch product data

Create the GraphQL query that fetches product data app/graphql/get_products.graphql:

query get_products($slug: String) {
  products: models(
    filter: {
      name: { value: "product" }
      deleted_at: { value: null }
      properties: [
        { name: "slug", value: $slug }
      ]
    }
    per_page: 100
  ) {
    total_entries
    results {
      name: property(name: "name")
      slug: property(name: "slug")
      description: property(name: "description")
      photo: custom_image(name: "photo") { url }
      price: property(name: "price")
    }
  }
}

Notes

  • If the optional slug argument is omitted, then it returns all products.
  • price is a property defined in the product Model Schema's .yml file, which is found in the model_schemas directory:
name: product
properties:
- name: name
  type: string
- name: slug
  type: string
- name: description
  type: string
- name: price
  type: float
- name: photo
  type: photo

Step 4: Display product details

Create the /product-details page that loads when visitors click on a product from /product-list above views/pages/product-details.liquid:

---
slug: product-details
layout_name: application
---

{% graphql g = 'get_products', slug: params.slug2 %}
{% assign product = g.products.results.first %}

<h1>{{ product.name }}</h1>
<amp-img src="{{ product.photo.url }}" layout="responsive"></amp-img>
<p>${{ product.price }}</p>
<p>{{ product.description }}</p>

Notes

  • The page at /product-list passes the product slug to this page through the URL, which is formed as /product-details/<listing_slug>
  • Then you pass on the product's slug to the GraphQL query (slug: params.slug2), to find the product details.
  • This is the same GraphQL query that you used on the other page to retrieve all products, but this time you call it with the product slug to find just one product.

Questions?

We are always happy to help with any questions you may have.