Headless WordPress + Vue 3 + Apollo + GraphQL

This is old stuff by now 🙁

Headless WordPress in Vue 3 - blog post background

So, continuing my experiments with getting everything working in Vue 3, here’s the next topic: setting up GraphQL, a headless WordPress, and Vue 3.

The documentation for Vue 3 and the corresponding Apollo client for GraphQL doesn’t seem to be up to date, so… a couple of solutions to that not working.

First, I recommend this great video tutorial by Jason Lengstorf and Natalia Tepluhina, it will get you started.

However, if you don’t have 1 hour and 32 minutes of great video content, here’s a quick fix/setup to get GraphQL working with WordPress and Vue 3.

Headless WordPress prerequsities

A working installation of WordPress, somewhere, anywhere. For this, a basic setup will do. The only addition to the default installation of WordPress is one plugin, namely:

Add the GraphQL plugin, also available through the plugins search in WordPress.

A custom headless WP theme

For this project, I have created and activated a custom WordPress theme. It consists of a (so far) empty index.php file and a style.css file. They contain nothing.

I’m accessing the WordPress content through Vue, but there’s no automation set up. So if Vue is running on http://localhost:8080, that’s where content is served from.

Vue 3 setup and packages 📦

Install Vue 3/setup your project (with your default, I didn’t use TypeScript for this so perhaps some adjustments are needed).

With your package manager of choice, add the following packages to your dependencies:

Here’s my package.json:

{
  "name": "app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@apollo/client": "^3.3.15",
    "@vue/apollo-composable": "^4.0.0-alpha.10",
    "core-js": "^3.6.5",
    "graphql": "^15.5.0",
    "vue": "^3.0.0",
    "vue-router": "^4.0.0-0",
    "vuex": "^4.0.0-0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-airbnb": "^5.0.2",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-vue": "^7.0.0",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2"
  }
}

Let’s get crackin’

main.js

import { createApp, provide, h } from 'vue';
import {
  ApolloClient, InMemoryCache, gql,
} from '@apollo/client/core';
import { DefaultApolloClient } from '@vue/apollo-composable';
import App from './App.vue';
import router from './router';
import store from './store';

const defaultClient = new ApolloClient({
  uri: <span style="color: #ff6600;">'insert-your-graphql-endpoint-here, like https://address-to-wordpress-installation/graphql'</span>,
  cache: new InMemoryCache(),
});

const app = createApp({
  setup() {
    provide(DefaultApolloClient, defaultClient);
  },
  render() {
    return h(App);
  },
});

app.use(store);
app.use(router);

app.mount('#app');

At one point, I had the following error message:

This dependency was not found:

* react in ./node_modules/@apollo/client/react/context/ApolloConsumer.js, ./node_modules/@apollo/client/react/hooks/useApolloClient.js and 1 other

The key to solving this was to do the import from “core”, as so:

import {
  ApolloClient, InMemoryCache, gql,
} from '@apollo/client/core';

The GraphQL query

As in the video tutorial, I created a separate GraphQL file with the query.

Inside my Vue installation, I have this file: app/src/graphql/allPosts.query.gql

The contents of that file look like this (copy-paste from WordPress GraphQL IDE):

query GET_ALL_POSTS {
  posts {
    nodes {
      postId
      content(format: RENDERED)
      dateGmt
      tags {
        edges {
          node {
            name
            link
          }
        }
      }
      title
    }
  }
}

The component

And then, my component in Vue looks like this:

<template>
  <div class="single-blog-post" v-if="posts">

    <article v-for="post in posts.nodes" :key="post.postId">
      <h1>{{post.title}}</h1>
      <div v-html="post.content"></div>
      <hr/>
    </article>

  </div>
</template>

<script>
import { useQuery, useResult } from '@vue/apollo-composable';
import GET_ALL_POSTS from '../graphql/allPosts.query.gql';

export default {
  name: 'ArchivePosts',
  setup() {
    const { result } = useQuery(GET_ALL_POSTS);
    const posts = useResult(result, null, (data) => data.posts);

    return {
      posts,
    };
  },
};
</script>