Decoupling Drupal with Gatsby

Static site generators have evolved in the last few years. Names like Jekyll, Gatsby, Hexo, Hugo … has become more and more popular. They have been chosen by more developers when it comes to simple web/blog solution. They need very minimal server setup and low maintenance cost. However, writing content in Markdown is not very welcomed by most writers.
This article is originally posted at Evolving Web
On the other hand, content management systems (CMS) such as Drupal, Wordpress … can provide a very powerful back-end, WYSIWYG content editor … which helps managing content systematically and effortlessly. However, maintaining CMS requires hosting, web server, database … and precaution for security risks.

Gatsby stands in between the simplicity-robustness of static site, and the versatile back-end of content management system. Using Gatsby means you can host the CMS in-house and publish content generated by Gatsby as static website.

In this tutorial, we talk about using Drupal content to power Gatsby. We'll borrow content of this awesome blog post to create a list of coffee types in Drupal, then transfer the list content to Gatsby.

This ultimate goal can be achieved with 3 steps:

  1. Build a Drupal server
  2. Build a Gatsby site that fetches content from Drupal server
  3. Buid the Gatsby site and publish it

1. Build a Drupal server

TL;DR: For those who are already familiar with Drupal already, this step is very simple. We create a content type named coffee with 3 fields:

  • Title - name of the coffee
  • Body - description of the coffee
  • Image - the name said itself

If you're new with Drupal, follow these step:

From the Drupal site, create the content type coffee: go to Structure > Content types > Add content type, then enter basic declarations:

  • Name: Coffee
  • Description: leave empty if wished -- optional
  • Submission form settings > Title field label: Title -- optional
  • Publishing options > Default options: leave as default -- optional
  • Display settings: uncheck the Display author and date information -- optional
  • Menu settings: uncheck all Available menus -- optional

Then hit the Save and manage fields continue.

Create content type coffee

By default, the newly created content type comes with Body field of type Text (formatted, long, with summary). We'll use this field as the coffee description, so leave it untouched.

The missing part is the image field, so let's add one: click on Add field, select Image: field_image in Re-use an existing field then click Save and continue. The next page shows some configurations of field_image. In this tutorial, we'll leave all values as default.

The final content type should look like this 🎉:

Coffee content type

Now it's time to create some Coffee nodes, just a few dozens of copy/paste to do.

Turn Drupal into API server using jsonapi

Download and enable module jsonapi and jsonapi_extras then navigate to http://[your-site]/jsonapi to see data of your Drupal site.

Drupal jsonapi endpoint

I use JSON Viewer Chrome extension to view JSON data in a better format

Note:

However, now if you logout, you'll get a 406 error when viewing /jsonapi endpoint, because you don't have permission to view JSON data.

JSON API - 406 error

By default, jsonapi endpoint is at /jsonapi, and data is not accessible to anonymous users by default. We'll need to give permission to Access JSON API resource list to Anonymous user. Go to /admin/people/permissions and search for "JSON API", then check the checkboxes to allow Anonymous user and Authenticated user to access JSON API data.

JSON API permission

Without this permission, Gatsby won't be able to communicate with Drupal and throw out an 406 error.

Module jsonapi_extras will give you more control on the API settings (at /admin/config/services/jsonapi/settings) such as changing the endpoint. And jsonapi_extras will help you avoid error 405 - Method Not Allowed when query data from Gatsby.

Now we are done with the server site set up. Let's switch to the Gatsby site.

2. Build a Gatsby site that fetches content from Drupal server

2.1. Build a Gatsby site

First, make sure you have node and npm installed on your computer. Verify it by typing node -v and npm -v into Terminal

$ node -v
v10.1.0
$ npm -v
5.6.0

Install Gatsby's command line tool

npm install --global gatsby-cli

Create a new Gatsby site and run it, I'll call my Gatsby site coffees.gatsby

$ gatsby new coffees.gatsby // create folder coffees.gatsby and clone Gatsby code into it
$ cd coffees.gatsby
$ gatsby develop // start hot-reloading development environment

By default, new Gatsby site is accessible at localhost:8000

Gatsby site

2.2. Fetch content from Drupal server

At this step, we'll be creating a new simple page /coffees that displays all coffee types in Drupal site.

2.2.1. Create the /coffees page

Create a new page in Gatsby is as simple as create a new JS file. All Gatsby pages should be stored in /src/pages. The filename will be the page URL. In this case, we create file coffees.js in /src/pages and add the following code into coffees.js

import React from "react"
const CoffeesPage = () => (
  <div>
    <h1>Different types of coffee</h1>
  </div>
)
export default CoffeesPage

This simple code does one thing: create a new page at /coffees and content is a heading <h1> saying "Different types of coffee"

Coffee page

2.2.2 Query Drupal content using GraphQL

In order to pull data from Drupal 8 site, we need to install gatsby-source-drupal plugin

// in your coffees.gatsby folder
$ npm install --save gatsby-source-drupal

Configure gatsby-source-drupal plugin

// In gatsby-config.js
plugins: [
  ...
  {
    resolve: 'gatsby-source-drupal',
    options: {
      baseUrl: 'http://dcmtl2018-demo.server/',
      apiBase: 'jsonapi',
    },
  }
],

After adding the plugin configuration, the site should still functioning. If Gatsby yells error 406, check for permission on Drupal site; if Gatsby yells error 405, make sure module jsonapi_extras enabled.

Build GraphQL to query all Coffee nodes from Drupal

Gatsby comes with an in-browser tool for writing named GraphiQL, validating and testing GraphQL queries and it can be found at localhost:[port]/___graphql, in our case it's localhost:8000/___graphql

If all the configuration are rights, by default, the autocompletion function (CTRL + Space) of GraphiQL will be very handy to start with.

We'll be querying all Coffee nodes in this tutorial

GraphQL

After building the query successfully, let's go back to the coffees.js file to execute the query.

Add the following code to the bottom of the file:

export const query = graphql`
  query allNodeCoffee {
    allNodeCoffee {
      edges {
        node {
          id
          title
          body {
            value
            format
            processed
            summary
          }
        }
      }
    }
  }
`

Then update the const CoffeesPage to display title and body content:

const CoffeesPage = ({data}) => (
  <div>
    <h1>Different types of coffee</h1>

    { data.allNodeCoffee.edges.map(({ node }) => (
      <div>
        <h3>{ node.title }</h3>
        <div dangerouslySetInnerHTML={{ __html: node.body.value }} />
      </div>
    ))}
  </div>
)

Thanks to hot-reloading, we could see the sweet fruit of the work right after saving the file

Query and render data

Rendering images in Gatsby

Now we have to query the image URL from Drupal, add the image to the page. To do so, we need to install few more plugins and their dependencies;

npm install --save gatsby-transformer-sharp gatsby-plugin-sharp gatsby-image

Update the gatsby-config.js with new plugins:

plugins: [
  ...
  'gatsby-transformer-sharp',
  'gatsby-plugin-sharp',
],

In GraphiQL, try updating the current query to get image field. Here we need to specify the image dimensions (height and width). In GraphiQL preview screen, we should be able to see image field data.

{
  allNodeCoffee {
      edges {
        node {
          id
          title
          body {
            value
            format
            processed
            summary
          }
          relationships {
            field_image {
              localFile {
                id
                childImageSharp {
                  resolutions(width:220, height:140) {
                    base64
                    tracedSVG
                    aspectRatio
                    width
                    height
                    src
                    srcSet
                    srcWebp
                    srcSetWebp
                    originalName
                  }
                }
              }
            }
          }
        }
      }
  }
}

Now let's use this new query in coffees.js and print out the image

In order to use Image in Gatsby, we need to

  1. import gatsby-image:
import Img from "gatsby-image"
  1. Update the query:
export const query = graphql`
  query allNodeCoffee {
    allNodeCoffee {
        edges {
          node {
            id
            title
            body {
              value
              format
              processed
              summary
            }
            relationships {
              field_image {
                localFile {
                  id
                  childImageSharp {
                    resolutions(width:220, height:140) {
                      base64
                      tracedSVG
                      aspectRatio
                      width
                      height
                      src
                      srcSet
                      srcWebp
                      srcSetWebp
                      originalName
                    }
                  }
                }
              }
            }
          }
        }
    }
  }
`
  1. Print out image: now that we have the image information (path, data, dimensions ...) from the query, we can tell Gatsby to render it out using the following code:
const CoffeesPage = ({data}) => (
  <div>
    <h1>Different types of coffee</h1>
    { data.allNodeCoffee.edges.map(({ node }) => (
        ...
        <Img resolutions={node.relationships.field_image.localFile.childImageSharp.resolutions}
          key={node.relationships.field_image.localFile.id} />
        ...
    ))}
  </div>
)

And voila!

Render images

So far, we have done:

  • Create an API server with Drupal and jsonapi, jsonapi_extras
  • Create a Gatsby site with page coffees.js that "reads" content from Drupal server

Let's move the the last step of the tutorial: publish Gatsby site.

3. Build the Gatsby site and publish it

Gatsby names itself as a static static site generator, meaning its main purpose is to generate a bunch of static HTML, JS, CSS and images files that can act as a static site. This action can be done by only one command:

$ gatsby build

Once finished, checkout /public folder to see result of your hard work along this long tutorial. Deploying your site is now simply copy/push contents in /public to server.

Conclusion

So far, in this tutorial, we get to know how to:

  • Create a new Gatsby site
  • Install new plugin in Gatsby
  • Use GraphiQL to write, validating and test GraphQL query
  • Use gatsby-image to optimize images then render image in Gatsby page