Headless WordPress with Gatsby

My two blogs Artifact and Otakism have been on Hexo for around 5 years now. Hexo has been working great in terms of cost – I’ve hosted them for free at services like Netlify or Github pages. I also have nothing to complain about the writing experience thanks to Markdown. However, there has always been one pain point about Hexo and other static site generators alike, that is the lack of a proper content management system (CMS). To be more specific, it is hard to find and edit old posts, because they are files named with dash-separated slugs in a giant directory. Fortunately, I solved this pain recently at no cost but a bit of weekend time.

My solution uses a private WordPress.com site as the back-end CMS, and a Gatsby generated static site sourcing data from the WordPress. This architecture is referred to as headless CMS nowadays. Certainly, it is not limited to the Gatsby WordPress combo. Technically any CMS with a content API can be used with any static generators to achieve this architecture. The difference comes down to how well it is supported on each side, so that we can build with minimum overhead.

I chose WordPress.com as the CMS because it is free. Another popular blogging platform Ghost also natively supports being a headless CMS. They provided good documentation as well. But it is much harder to host Ghost (stably) for free.

The choice of Gatsby mainly comes to how well it supports WordPress as a headless CMS. Gatsby supports it officially with gatsby-starter-wordpress and gatsby-source-wordpress. The former is a boilerplate app which can also be a great reference should you wish to DIY. The latter is the official plugin that wraps WordPress REST API with GraphQL that Gatsby is based on.

I don’t want to make this post a tutorial for setting up this Gatsby-Wordpress headless CMS architecture. I can’t beat the official documentation that is already very easy to read. If you want to set up something similar, just follow the links in this post. I do have a few tips that are not explicitly documented but worth sharing.

Query only the required resources

Make sure to request only the required WordPress resources to speed up Gatsby build. It does not break anything to request all of them, but considering that Netlify free tier has a limit on monthly build time, it is wise to be more frugal.

{
  options: {
    includedRoutes: [
      `**/posts`,
      `**/pages`,
      `**/categories`,
      `**/tags`,
      `**/users`,
    ],
  }
}

Paginated categories and tags page

The official gatsby-starter-wordpress has examples for categories and tags page, but those are not paginated. Here are the paginated version in gatsby-node.js:

const { paginate } = require('gatsby-awesome-pagination');
const createWordpressTags = ({ graphql, actions, publishedPosts }) => {
  return graphql(`
    {
      allWordpressTag(filter: { count: { gt: 0 } }) {
        edges {
          node {
            id
            name
            slug
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      result.errors.forEach(e => console.error(e.toString()))
      throw new Error(result.errors);
    }

    const tagTemplate = path.resolve('./src/templates/tag.js');
    const { createPage } = actions;
    const tags = result.data.allWordpressTag.edges;

    tags.forEach((edge) => {
      const tagPath = `/tags/${edge.node.slug}`;

      paginate({
        createPage,
        items: publishedPosts.filter(postEdge => {
          if (!Array.isArray(postEdge.node.tags)) {
            return false;
          }
          for (let tag of postEdge.node.tags) {
            if (tag.slug === edge.node.slug) {
              return true;
            }
          }
        }),
        itemsPerPage: 12,
        pathPrefix: ({ pageNumber }) => {
          return pageNumber === 0 ? `${tagPath}/` : `${tagPath}/page`
        },
        component: tagTemplate,
        context: {
          name: edge.node.name,
          slug: edge.node.slug,
        },
      });
    });
  });
};

Paginated categories work in the same way.

Set up build hook

Once your WordPress.com site is up and you have a Gatsby site deployed to Netlify, you might wonder how to trigger the build automatically. This can be easily set up with web hook.

Go to the Netlify dashboard. Under Site Setting > Build & Deploy > Build hooks, create a new build hook. Copy the generated url.

Go to the traditional WP Admin of your WordPress site. Go to Settings > Webhooks from the sidebar. Then click the Add webhook button. You will create 2 webhooks, one for publish_post, the other for publish_page. Enter the webhook url copied from Netlify. Choose anything from the fields list. It does not matter because Gatsby does not rely on these data to re-build the site.

By doing so, WordPress will invoke the Netlify build hook url, and Netlify will trigger a deployment that pulls the latest data from WordPress.