Skip to content

Instantly share code, notes, and snippets.

@fmt
Last active March 20, 2020 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fmt/e57fdf5626753907c1e3c8ca1c77790a to your computer and use it in GitHub Desktop.
Save fmt/e57fdf5626753907c1e3c8ca1c77790a to your computer and use it in GitHub Desktop.
Shopify Product RSS feed, to be called in /collections/all?view=rss
{% layout none %}{% comment %}
/*
* Shopify Product RSS feed, also needs Cloudflare Workers
* for Content-Type fix to application/rss+xml, because
* Shopify only supports text/html
*
* Copyright (c) 2018 @fmt github.com/fmt
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
* Based on @freakdesign blog feed gist
*/
{% endcomment %}{% capture feedSettings %}
{% assign feedTitle = 'Product Feed' %}
{% assign feedDescription = 'Store Products RSS Feed' %}
{% comment %}
How many products to show
{% endcomment %}
{% assign productCount = 1000 %}
{% comment %}
Either to include the product collections
{% endcomment %}
{% assign showCollections = true %}
{% comment %}
Set image size
{% endcomment %}
{% assign imageSize = 'grande' %}
{% comment %}
Either to truncate description and content, after how many words
{% endcomment %}
{% assign truncateDescription = true %}
{% assign truncateContent = false %}
{% assign truncateAfter = 50 %}
{% comment %}
Force content to plain text, if HTML breaks syntax
{% endcomment %}
{% assign plainText = false %}
{% comment %}
Remove CDATA tags, needed if the content already has them
{% endcomment %}
{% assign removeCdataTags = true %}
{% endcapture %}<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:media="http://search.yahoo.com/mrss/"
>
<channel>
<title>{{ shop.name }} - {{ feedTitle }}</title>
<atom:link href="{{ canonical_url }}?view=rss" rel="self" type="application/rss+xml"/>
<link>{{ canonical_url }}</link>
<description>{{ feedDescription }}</description>
<lastBuildDate>{{ collections.all.products.first.created_at | date: "%a, %d %b %Y %H:%M:%S %z" }}</lastBuildDate>
<language>{{ shop.locale }}</language>
<sy:updatePeriod>daily</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
{% paginate collections.all.products by productCount %}
{% for product in collections.all.products %}
<item>
<title>{{ product.title }}</title>
<link>{{ shop.url }}{{ product.url }}</link>
<pubDate>{{ product.created_at | date: "%a, %d %b %Y %H:%M:%S %z" }}</pubDate>
<dc:creator><![CDATA[{{ product.author | default: shop.name }}]]></dc:creator>
{% if showCollections %}
{% assign prodCollections = '' %}
{% for collection in product.collections %}
{% assign prodCollections = prodCollections | prepend: collection.title | prepend: ',' %}
{% endfor %}
{% assign prodCollectionsLast = prodCollections | remove_last: ',' %}
<category><![CDATA[
{{ prodCollectionsLast | remove_first: ',' }}
]]></category>
{% endif %}
<guid isPermaLink="true">{{ shop.url }}{{ product.url }}</guid>
<description><![CDATA[
{% if truncateDescription %}{{ product.content | strip_html | truncatewords: truncateAfter }}
{% else %}{{ product.content | strip_html }}
{% endif %}
]]></description>
{% assign productContent = product.content %}{% if removeCdataTags %}{% assign productContent = productContent | remove:'<![CDATA[' | remove:']]' %}{% endif %}
{% if truncateContent %}<content:encoded>{{ productContent | strip_html | truncate_words:truncateAfter }}</content:encoded>
{% elsif plainText %}<content:encoded>{{ productContent | strip_html }}</content:encoded>
{% else %}<content:encoded><![CDATA[{{ productContent | replace:'<br>','<br/>' }}]]></content:encoded>
{% endif %}
{% if product.featured_image %}
<media:content url="https:{{ product.featured_image | img_url: imageSize }}"/>
{% endif %}
</item>
{% endfor %}
{% endpaginate %}
</channel>
</rss>
@fmt
Copy link
Author

fmt commented Oct 18, 2018

Because Shopify only supports text/html templates, we need to setup @cloudflare Workers (or @aws Lambda@Edge) so that the Content-Type returns as application/rss+xml. This is my take on that, using CF Workers:

addEventListener('fetch', event => {
  if( event.request.url.indexOf('/collections/all?view=rss') === -1 ) {
      return true
  }
  event.passThroughOnException()
  event.respondWith(handleRequest(event.request))
})
async function handleRequest(req) {
    let reqUrl = req.url
    if (reqUrl.indexOf('/collections/all?view=rss') > -1) {
      let res = await fetch(req.url, req)
      let newHdrs = new Headers(res.headers)
      newHdrs.set("Content-Type", "application/rss+xml; charset=UTF-8")
      return new Response(res.body , {
        status: res.status,
        statusText: res.statusText,
        headers: newHdrs
      })
    }
    return fetch(reqUrl, req)
}

@ezable
Copy link

ezable commented Mar 20, 2020

Question - It looks like I create a collection.liquid.rss for the rss.

In what area of Shopify do I create the file @cloudflare code? Does it go in the snippet section?

@ezable
Copy link

ezable commented Mar 20, 2020

I tried to create this feed for Pinterest. I get this error. "Feed can not be parsed". Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment