Renewing ExpressionEngine with Gatsby
ExpressionEngine has had a rocky few years, and although it may not be a go-to choice for agencies anymore, it still powers many websites.
One way to give new life to an ExpressionEngine site is to build new sections of a site using Gatsby rather than ExpressionEngine templates. Doing so allows you to mostly bypass ExpressionEngine’s template engine and utilise front-end developers who probably don’t have much ExpressionEngine experience these days.
What is Gatsby, and why?
Gatsby is a React powered framework that allows you to pull in data from anywhere and build flexible and modern templates which are built (like a static site generator) into HTML files.
This technique isn’t necessarily right for every website, but it works particularly well when you have a microsite, landing page, or campaign that needs to be powered by ExpressionEngine.
We used this technique for The Green Parent’s subscription microsite where we needed to bring in content from ExpressionEngine and Shopify, but it didn’t make sense to build more outdated ExpressionEngine templates or learn how to develop good Liquid templates. Instead, we used Gatsby to pull in content from both ExpressionEngine and Gatsby to build the microsite in a similar way we might with modern Craft CMS websites.
Part one: the data source
A template needs to be created in ExpressionEngine that outputs any data we will want on the Gatsby site. This part would need a developer familiar with ExpressionEngine to do, but it means the wider project can be handed over to any front end developer that knows Gatsby after this step.
The form your data takes is up to you and will be influenced by your data model. Our needs are quite simple, we need to bring photos of the magazine issues, testimonials, and information about a free gift. So we make an ExpressionEngine template that outputs JSON.
<?phpheader("Content-Type:application/json");?>{{exp:channel:entries channel="free-gift" dynamic="no"}"freeGifts": {"image": "https://thegreenparent.co.uk{exp:ce_img:pair src="{free-gift_image}" width="220" height="220" crop="yes" allow_scale_larger="yes"}{made}{/exp:ce_img:pair}","title": "{title}","text": "{free-gift_text}"},{/exp:channel:entries}"whySubscribe": {exp:channel:entries channel="why-subscribe" dynamic="no"}"{why-subscribe_text}"{/exp:channel:entries},"testomonials": [{exp:channel:entries channel="testimonial" limit="2" orderby="random" dynamic="no"}{"photo": "https://thegreenparent.co.uk{exp:ce_img:pair src="{testimonial_photo}" width="150" height="150" crop="yes" allow_scale_larger="yes"}{made}{/exp:ce_img:pair}","testimonial": "{testimonial_quote}","author": "{title}"}{if count != total_results},{/if}{/exp:channel:entries}],"spreads": [{exp:channel:entries channel="issues" limit="3" dynamic="no"}{"issue": "{title}","frontCover": "https://thegreenparent.co.uk{exp:ce_img:pair src="{cf_issues_cover}" width="260" height="340"}{made}{/exp:ce_img:pair}","innerPages": [{cf_issues_spread-images}"https://thegreenparent.co.uk{exp:ce_img:pair src="{cf_issues_spread-images:image}" width="615" height="403"}{made}{/exp:ce_img:pair}"{if cf_issues_spread-images:count != cf_issues_spread-images:total_rows},{/if}{/cf_issues_spread-images}]}{if count != total_results},{/if}{/exp:channel:entries}]}
This produces a JSON feed like this…
{"freeGifts": {"image": "https://thegreenparent.co.uk/images/made/assets/uploads/main/Screenshot_2019-09-16_at_15.51_.41__220_220_s_c1.png","title": "Subscribe to The Green Parent magazine a delicious body wash from Odylique as a gift*","text": "Envelop yourself in this creamy, moisturising wash complemented with the fragrant organic essential oils of ylang ylang and orange. Shop the range at odylique.co.uk *Available to UK subscribers only"},"whySubscribe": "This lush magazine is a real treat to receive. And it’s packed full of positive, inspiring content, helping you be a calmer, happier, more abundant parent. Order today and get every issue delivered direct to your door before it appears in the shops.","testomonials": [{"photo": "https://thegreenparent.co.uk/assets/uploads/main/sarah-churcher.jpg","testimonial": "The biggest benefit has been knowing I'm not alone in my parenting choices.","author": "Sarah Churcher"},{"photo": "https://thegreenparent.co.uk/assets/uploads/main/samantha-quinn.jpg","testimonial": "This magazine is such an inspirational read. It’s a great resource for anyone looking for a more holistic approach to parenting. I teach baby massage and constantly recommend the magazine to my clients.","author": "Samantha Quinn"}],"spreads": [{"issue": "Issue 91","frontCover": "https://thegreenparent.co.uk/images/made/assets/uploads/issues/tgp_91_260_340.jpg","innerPages": ["https://thegreenparent.co.uk/assets/uploads/issues/Issue_91.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_912.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_913.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_914.jpg"]},{"issue": "Issue 90","frontCover": "https://thegreenparent.co.uk/images/made/assets/uploads/issues/tgp90_260_341.jpg","innerPages": ["https://thegreenparent.co.uk/assets/uploads/issues/90.jpg","https://thegreenparent.co.uk/assets/uploads/issues/902.jpg","https://thegreenparent.co.uk/assets/uploads/issues/903.jpg","https://thegreenparent.co.uk/assets/uploads/issues/904.jpg","https://thegreenparent.co.uk/assets/uploads/issues/905.jpg","https://thegreenparent.co.uk/assets/uploads/issues/906.jpg"]},{"issue": "Issue 89","frontCover": "https://thegreenparent.co.uk/images/made/assets/uploads/issues/TGP88_cov_web_260_340.jpg","innerPages": ["https://thegreenparent.co.uk/assets/uploads/issues/Issue_895.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_894.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_892.jpg","https://thegreenparent.co.uk/assets/uploads/issues/Issue_893.jpg"]}]}
Part two: Consuming the data
One of the best parts about Gatsby is that we can pull in data from various sources and interact with any of them using a common GraphQL syntax.
ExpressionEngine doesn’t have GraphQL, but we can tell Gatsby to load our JSON feed and convert that feed into one or more GraphQL node. It will depend on your data model how you actually consume it, on a simple page like ours the following example may well work, but if you’re pulling in multiple entries you may need to loop through results to create multiple nodes.
We will use the gatsby-nodes.js
file to tell Gatsby to speak to ExpressionEngine and create the node. To do this we’re using the Axios library to grab the JSON for us.
const axios = require("axios")const crypto = require("crypto")let pluginActionsexports.onPreInit = ({ actions }) => {pluginActions = actions}exports.sourceNodes = async () => {await getExpressionEngineData()return}const getExpressionEngineData = async () => {const { createNode } = pluginActionsconst subscribeData = axios.get(`FEEDURLGOESHERE`)const subscribeDataResponse = await subscribeDataconst subscribeDataResponseData = subscribeDataResponse.datasubscribeDataResponseData.id = "ee_cms_data"subscribeDataResponseData.internal = {contentDigest: crypto.createHash(`md5`).update(JSON.stringify(subscribeDataResponseData)).digest(`hex`),type: "cmsdata",}createNode(subscribeDataResponseData)}
And then querying the GraphQL on our template like so…
export const IndexPageQuery = graphql`{cmsdata {spreads {issuefrontCoverinnerPages}whySubscribetestomonials {authorphototestimonial}freeGifts {imagetexttitle}}}`
Which allows us to use the data within the rendered template like so…
{cmsdata.testomonials.map(testimonial => (<Testimonialphoto={testimonial.photo}author={testimonial.author}text={testimonial.testimonial}/>))}
Conclusion
Modern JAMstack tools like Gatsby can give new life to ExpressionEngine, especially within teams that don’t have an appetite to build more ExpressionEngine templates or have more complex front-end requirements.
It’s also a great technique if this part of the website is expected to get a lot of traffic because it can be hosted on Netlify’s CDN and scale effortlessly without worrying about complicated ExpressionEngine caches.