Getting started with Gatsby.js and Ibexa DXP (eZ Platform)
The headless CMS concept is to the point in the sense that it only focuses on pure content management. This leaves content delivery and presentation to another system, i.e. the head. Following our latest blog post on hosting your CaaS on eZ Platform Cloud, it's a good time to focus on the other end of the story - the head for your headless CMS.
A content API can feed almost anything, from a mobile app to a device such as a connected food processor. A web app is the most likely scenario, but even in this space there are a lot of different methods for implementation. Static site generators are one option that have gained recognition in recent years due to their unmatched performance and high security. They take raw content as input and output a set of HTML files that can be hosted anywhere.
Gatsby.js is a popular static site generator built with JavaScript. It is a highly opinionated toolkit which uses React.js for the UI layer and GraphQL as an internal query language. Both of these technologies are also used internally in eZ Platform, which makes Gatsby an approachable technology stack for developers who are used to extending eZ Platform.
Getting to grips with Gatsby.js
As Gatsby is built on Node.js and React.js, you should be comfortable with contemporary JavaScript and their related concepts. If you are not, I recommend that you spend some time to get to know the basics before diving head-first into a customer project.
One of the reasons behind the popularity of Gatsby is the developer experience. The team has done an amazing job of providing tooling to make it easy to get started. There's no point in repeating everything here, so for detailed instructions on be sure to check the project documentation and the getting started tutorial. Here are the first steps in a nutshell:
- Install Node.js
- Install Gatsby CLI tool
- Create a new Gatsby project
Once you've got a project set up, you can run the development with "gatsby develop". This will spin up a local development server. The default project has some basic content and styles applied, so it is best to get started by examining the directory structure:
- root
- src
- components
- images
- pages
- src
All of your source code is laid out in the src directory. The components subdirectory holds items such as stylesheets, template snippets and the master template, layout.js, which similar to what you would have as pagelayout.html.twig in your eZ Platform project using Twig for templating. You can add more general purpose React.js components for navigation, etc. if you need to in this directory when you add more features to your project.
The images directory contains any images that are a part of your application, such as a site logo, etc. Actual content imagery usually comes from somewhere else in real world implementations. The pages directory is an essential part of the Gatsby API. Any components placed in it will be available with a route based on the front page.
Consider this page component (placed in src/pages/hello-world.js):
import React from "react" import { Link } from "gatsby" import Layout from "../components/layout" import SEO from "../components/seo" const HelloWorldPage = () => ( <Layout> <SEO title="Hello world" /> <h1>Hello world!</h1> <p><Link to="/">front page</Link></p> </Layout> ) export default HelloWorldPage
It is exposed via http://localhost:8000/hello-world and contains the following functionality:
- Uses the Layout component from Layout component (src/components/layout.js)
- Overrides the title value for the SEO component (src/components/seo.js)
- Has some static content (<h1>Hello world!</h1>)
- Links to the front page using the built in Link API
I found the best approach to poke around a little bit, adding some pages, new components, doing some style changes, etc. to get a basic feel for how the core Gatsby API works. If the JavaScript or JSX syntax is not familiar to you, there are plenty of resources online to get you started: Syntax and Feature Overview, Introducing JSX, ES6 for everyone…
Once you are done, you can try doing a production build with the command "gatsby build". This will export your site to a set of static assets to the public directory, which you can then copy anywhere you can host static HTML files on the public internet. You will not need to run any specific server application. That is the key benefit of using a static site generator.
Integrating eZ Platform using GraphQL
You can use the basic Gatsby API to build yourself a simple site, but for more serious use you'll probably want to use a data source. This can be anything from a directory with Markdown files to data coming from a Google spreadsheet. In our case we want to integrate the eZ Platform GraphQL API to our Gatsby site as a content source.
Before we continue it is worth noting that Gatsby uses GraphQL as an internal API. This has nothing directly to do with the eZ Platform GraphQL implementation, but it does make them straightforward to integrate. Gatsby uses 'Source Plugins' to integrate external systems to the internal GraphQL API: Creating a Source Plugin
Source plugins are needed when you want to adjust a custom protocol, but because eZ Platform exposes a standard GraphQL API we can use a more generic solution: gatsby-source-graphql. Instead of writing a custom source plugin we need to install the generic plugin and configure our endpoint in the main configuration file, gatsby-config.js:
{ resolve: "gatsby-source-graphql", options: { typeName: "ezplatform", fieldName: "ezplatform", url: "https://yourezplatforminstall.com/graphql", }, },
Once this configuration is in place and you (re)start the development server, you should see the eZ Platform GraphQL API show in the development GraphQL console (located at http://localhost:8000/__graphql) as shown below:
I initially experienced some trouble with errors which were not very descriptive:
ERROR #11321 PLUGIN "gatsby-source-graphql" threw an error while running the sourceNodes lifecycle: [object Object] warn The gatsby-source-graphql plugin has generated no Gatsby nodes. Do you need it?
The cause was that my eZ Platform installation did not have the domain schema in place. This is generated automatically from your content repository with the following command:
./bin/console ezplatform:graphql:generate-schema
Another alternative reason for failure could be missing CORS headers. Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to grant browsers to consume resources across domains. If the Gatsby GraphQL Source Plugin fails to receive permission to consume a remote GraphQL in form of HTTP headers could fail.
Adding CORS headers for a Symfony application like eZ Platform can done by installing and configuring the NelmioCorsBundle to allow cross site requests to your endpoint:
paths: '^/graphql': allow_origin: ['*'] allow_headers: ['*'] allow_methods: ['GET']
Loading a single content item from eZ Platform
We've now explored the core concepts of Gatsby and connected eZ Platform. The next step is to combine the two and fetch some content to your Gatsby components from eZ Platform. The following GraphQL query fetches a single "Notice" element with the id 237:
{ ezplatform { content { notice(id: 237) { title body { html5 } } } } }
Copy the hello-world component to src/pages/hello-ez.js for extending it. To get our query executed by a page component we need to include the graphql from the gatsby package:
import { Link, graphql } from "gatsby"
Once this is place you need to wrap it using the provided graphql template literal:
export const query = graphql' { ezplatform { // rest of the query here '
To make it available to our page component function you need to pass in the data variable:
const HelloWorldPage = ({ data }) => (
Now the result of the GraphQL query is available and you can output the fields as needed:
<Layout> <SEO title={ data.ezplatform.content.notice.title } /> <h1>{ data.ezplatform.content.notice.title}</h1> { data.ezplatform.content.notice.body.html5 } <p><Link to="/">front page</Link></p> </Layout>
If you render this now, you will get some escaped markup for the html5 output type. To fix this you'll need to install the react-render-html package from NPM. Then include it in the component and pass any fields containing HTML through the provided function:
import renderHTML from 'react-render-html'; ... { renderHTML(data.ezplatform.content.notice.body.html5) }
With the above changes your component will now fetch content from eZ Platform dynamically. The full component source is available on GitHub: src/pages/hello-ez.js
Listing content and providing dynamic pages
Loading a single page's content and displaying is simple, but also very limited. For publishing blogs and other dynamic content, you will need to have generated listing pages. Creating the listing page is very similar to what we did above. Instead for querying for a single content item, we will query for many (the first 100, to be exact):
query { ezplatform { content { articles(first: 100) { edges { node { _content { id } title } } } } } }
Looping through the items in the listing component (src/pages/articles.js) is done as follows:
<ul> {data.ezplatform.content.articles.edges.map(({ node }, index) => ( <li><Link to={'/article/${node._content.id}'}>{node.title}</Link></li> ))} </ul>
Note that we are passing in the content object id in the Link component's "to" attribute. This is a reference to a dynamic route that we will need to create to be able to display the full article contents on its own page. Because we are generating a static set of HTML and not running a database query, we will need to create all content items before linking to them.
Gatsby can generate pages from a source before the development (or build process) begins. This is done using the Gatsby Node APIs via a JavaScript file located in the root directory of the project. In this file you will need to perform queries and configure how they should be made available. Below is a snippet from our project's gatsby-node.js file:
const postTemplate = path.resolve('./src/templates/article.js') result.data.ezplatform.content.articles.edges.forEach(edge => { createPage({ path: '/article/' + edge.node._content.id, component: slash(postTemplate), context: { id: edge.node._content.id, }, }) })
Here is a short description of what is happening above:
- Each result of our listing query is looped and a createPage function call is made
- The path property defines the path this page will be available in
- The component property defines the component used to display the item
- Context is used to pass values that can be used by the component
The component used to display a dynamic result is similar to components living in the page/ directory. The component does not automatically load any content, so you need to use the content object id in the component to execute a GraphQL query to return what you need:
query ($id: Int!) { ezplatform { content { article(id: $id) { title intro { html5 } body { html5 } } } } }
Outputting the results in your component is identical to standard page component:
<Layout> <SEO title={data.ezplatform.content.article.title} /> <h1>{data.ezplatform.content.article.title}</h1> {renderHTML(data.ezplatform.content.article.body.html5)} <p><Link to="/articles">Article listing</Link></p> </Layout>
Conclusion
Gatsby is a capable static site generation that comes out of the box with a nice theme and has great developer tools. It also integrates to many popular tools using plugins. Because of using the GraphQL protocol eZ Platform does not require a specific plugin to be developed.
Getting started is simple as the file based API makes adding pages simple and it is easy to pick up the basics of how React works for templating. However some more advanced features like dynamic page generation are not necessarily intuitive, but with the help of the documentation and available examples you can be sure to reach your goals for basic needs.
You can easily try the example we built above for yourself. The full source code is on GitHub and an eZ Platform Cloud instance serves the demo content for you.
If you've already decided to go for Gatsby as your front end, but are still in the process of selecting a Headless CMS, then be sure to read our free eBook: Headless CMS in the Enterprise
Discover which skills and technology are required to successfully implement Headless CMS in the Enterprise
Headless CMS in the Enterprise
This eBook covers the considerations you should take into account to have the right skills and technology in place as well as what to pay attention to when it comes to the cost of ownership to ensure successful digital transformation in your organization.