Create a WordPress TwentySeventeen theme with VueJS and GraphQL

Last year, we were involved in a project with the WordPress REST API. A client wanted to incorporate a WordPress blog in their corporate site. So we set up a WordPress backend for the blog authors, installed the WP REST API plugin and extended it. The front end developer would tell us which data he needed, and we would provide an endpoint for each of the http calls. A few very specific issues that we encountered with this decoupled setup, were author previews and SEO. But we managed to counter them also.

Although the project was successfully delivered, I still felt a little bit uncomfortable with it:

  • I had some reservations about the way we developed the REST endpoints: it felt a lot like ad hoc – coding. Each time a new feature request would come in, we had to provide a new endpoint. After a while then, it became obvious that some endpoints could be bundled in a bigger endpoint, and so on. But when you query such an endpoint, you always get the same amount of data, regardless of what you need. Of course that was a problem for the front end developer, but for me, it felt like we failed to offer him clean data.
  • We weren’t allowed to write the front end. So I was jealous of the front end developer: we had to write php and he could do some fancy javascripting.

Therefore I decided to do a little proof-of-concept project on myself: build a WordPress TwentySeventeen theme with VueJS and GraphQL.

GraphQL

I found out about GraphQL through a blog post: GraphQL in the age of REST API’s (https://medium.com/chute-engineering/graphql-in-the-age-of-rest-apis-b10f2bf09bba). GraphQL is a query language for API’s, developed by Facebook (http://graphql.org/).

To demonstrate how it works, I will compare it with a typical REST call: http://example.com/wp-json/v2/posts/35. Now imagine that I also need the categories that go with this post (with their link, slug, ?). In the default JSON response, I would only receive an array with category-ID’s. If I don’t want to make n + 1 requests for each parent-child relation, I have to customize the endpoint so that it also returns the category slugs, links, etc. This is of course doable, but we risk to end up with a lot of ad hoc endpoints. Moreover, every time we call this extended endpoint, we retrieve all the data, even if we don’t need it. Something doesn’t feel right, right?

Now let’s consider GraphQL. The previous request would then look like this one:

query postQuery( $id: Int, $tax: String) {
 wp_query {
  posts( ID: $id ) {
   title,
   content,
   slug,
   excerpt,
   terms( taxonomy: $tax ) {
    slug,
    term_id,
    url
   }
  }
 }
}

If I don’t need the categories, I just leave that piece out of the request. You model the question in the form of the requested answer. So you only have to customize on the client side. And you only get the data that you need, no less, no more. Another thing: you can pass parameters (like in the example above) to every field or nested object. And there is only 1 endpoint.

That is amazing! I want a GraphQL server! But how? Wait, when you work with WordPress for some time, this almost becomes a reflex: Is there a plugin for it? Yes, there is a plugin: graphql-wp of Tim Field (https://github.com/tim-field/graphql-wp). While a great plugin, it lacked a few things that I needed, and contained a lot of things that I didn’t need, so I decided to fork it and make a simplified version. I also included the bower dependencies, in order to make the plugin directly installable.

One more thing: there is a very handy Google Chrome add-on to help you explore queries on a GraphQL endpoint: GraphiQL. So I installed a WordPress backend, installed the plugin, and I could see the data coming in through GraphiQL. Great!

VueJS

Next step: build the front. Out of the myriad of JS frameworks, I chose VueJS, because the rumour went that it was fast, lightweight, easy to learn and fun to use. Well, I have to second all that: I really enjoyed the elegance and simplicity of this framework. Vue uses a VirtualDOM (like React) and a component-based templating system (like React and Angular). It comes with a bunch of very helpful add-ons like vue-router for routing and vuex for state management. Thanks to vue-cli, with a few commands, you have a working project setup with all the cool dev tools like babel, webpack, es6, unit testing, e2e testing etc. For a Vue app you need an index.html page and a main JS file to bootstrap the app. The template components are .vue files, which are a mix of HTML and JavaScript.

Now I can imagine you thinking: that is all very interesting, but how do you create a WordPress theme with all this?
Well, I started with a Home component that includes a few static components like Header and Footer, and a dynamic component that is substituted by vue-router:

<template>
 <div>
  <Header></Header>
  <PageHeader :post="post"></PageHeader>
  <div class="site-content-contain">
   <div id="content" class="site-content">
    <div class="wrap">
     <div class="very-changing-content">
      <transition name="fade">
       <router-view :key="$route.fullPath"></router-view>
      </transition>
     </div>
     <Aside></Aside>
    </div>
   </div>
   <SiteFooter></SiteFooter>
  </div>
  <SvgIcons></SvgIcons>
 </div>
</template>

Whenever the router is triggered, the router-view tag is replaced by other components, according to some router-rules that I defined. This way, you can mimic the WordPress permalink system. All you need to do, is provide components for each WordPress element: post, category, tag, search, custom posts, etc? For the data we need a GraphQL-client. I used Lokka for this (included in the npm in the GitHub-repo). You can define the queries as JS functions. They are promise-based, but I wrapped the answers in another promise, so I could call them asynchronously from within the components. Here is an example:

getTerms: function (taxonomy) {
 const query = `
  query termQuery($taxonomy: String) {
   wp_query {
    terms(taxonomy: $taxonomy) {
     slug,
     term_id,
     description
    }
   }
  }
 `
 const vars = { 'taxonomy': taxonomy }
 return new Promise((resolve, reject) => {
  client.query(query, vars).then((result) => {
   resolve(result)
  })
 })
}

With the result, the components can render themselves showing the data. I implemented the TwentySeventeen theme partially, with support for posts, pages, custom post types, ACF, menus, pagination, search, categories, tags, authors, featured video and header video. For SEO (of course not a real requirement for this experiment, but I wanted it working as POC), I created a htaccess file with some rules that rewrite everything bot-like (facebook-, slack-, twitter-, googlecrawlers) to the native theme. In the WordPress install I put a htaccess file which redirects everything to a landing page, except grapqhql- and botqueries. Server-side Rendering was not really an option. I could have included a Node.js server on my backend to provide SSR, but I wanted the wpgraphql-plugin to be complete and comprehensive, without specific hosting requirements.

So, I hope you enjoyed this post. I have a live demo here: http://prutstuin.be. The backend is here: https://testing.prutstuin.be (not much to see there, unless you?re a bot ;), and the code on GitHub: https://github.com/whuysmans. This project, I must say, is far from finished or production-ready :-)


Note: In January I gave a talk about this experiment at the Antwerp WordPress meetup. Report, video & slides on https://wpbelgium.be/antwerpen/werner-huysmans-experimenting-with-vue-js-and-twentyseventeen/