I've used Nuxt 3 with Storyblok and searched for a more flexible alternative. Since also it can get expensive and my customers prefer to use WordPress as a backend. It's easy to setup a Nuxt 3 project and connect it to a WordPress backend. We will do it with GraphQL for a few extras like TypeScript typing, but it can be done even more easily with the REST API WordPress provides out of the box.
Let's get started!
How? So basically, we will enable a GraphQL API that queries all the WordPress Data. We will use Vercel to host a Nuxt Page. So the “old” frontend the WordPress page is shipping can just be disabled.
Setup WordPress
First off, we need any kind of WordPress installation on a server. You can install it on your own or just create an account on those WaaS platforms, where you get everything for a few $s a month.
First, install those plugins:
WP GraphQL
Used to generate a GraphQL API endpoint and to have a Playground where you can try it out.
WP GraphQL Gutenberg
Used to generate additional endpoints to make the GraphQL API work well with Gutenberg. You get JSON data from Gutenberg blocks instead of the rendered WordPress HTML.
Do read the quick start of the GraphQL Plugin to get an idea of what it is and what you need to configure (not much!)
Setup Authentication
To query all fields and the schema, you have to first setup an Application Password. This will ensure that the password you use is only used for your nuxt app.
Your token will be in the format Basic XXXXX
.
You can generate it with this token generator, it is basically only your username + the new application password combined as a base64 string. Remember this token somewhere for later.
(Optional) – Issues with Wordpress Authentication?
On some hosters (those who serve PHP as CGI), you have to do additional configuration for the Authorization
header. Try out if it works without.
When not, continue:
Add a wp-content/plugins/auth-fix/index.php
into the WordPress Server and add this:
<?php
/*
Plugin Name: Auth Fix
Description: Fixes `MISSING_AUTHORIZATION_HEADER`: https://github.com/WordPress/application-passwords/issues/95#issuecomment-571586359
Version: 1.0.0
*/
add_filter('application_password_is_api_request', function ($api_request) {
if (empty($api_request)) {
return strpos($_SERVER['REQUEST_URI'], '/wp-json/') !== false;
}
return $api_request;
}, 10, 1);
Now activate the plugin in your UI and you are ready to go. You now have enabled secure access to the GraphQL Schema API.
Setup Nuxt
To get started quickly, create a new nuxt project with the getting started guide in the official Nuxt Docs. I'll wait here, I promise. Nuxt is great, so it won't take more than 2 mins.
What do we need?
After that, we need a library to do the GraphQL querying and maybe type generation for us. Let’s use nuxt-graphql-client for it. But could be any other.
This is how your nuxt.config.ts
should look like:
export default defineNuxtConfig({
typescript: {
shim: false,
},
modules: ['nuxt-graphql-client'],
})
You'll have to create a .env file in your project root:
GQL_HOST="https://your-wordpress-instance.com/graphql"
GQL_TOKEN="Basic xxx"
Everything else is fully automatic with this package. You are now querying your GraphQL endpoint as an authenticated user.
Your first query
Now basically you're already done with the basics! You can try to query your data.
Create a gql query file, e.g. queries/posts.gql
query Posts {
posts {
nodes {
id
title
date
slug
excerpt(format: RAW)
}
}
}
Everything will be automatically generated, so you can just create a new page, e.g. pages/index.vue
:
<template>
<div class="container">
<h1 class="text-4xl">Welcome to your page!</h1>
<div v-if="data">
<pre>{{ data }}</pre>
</div>
</div>
</template>
<script lang="ts" setup>
const { data } = await useAsyncGql({
operation: 'Posts',
})
</script>
And voilá, you have a list of your WordPress posts in Nuxt. Awesome! 🎉
Next Steps
Now, you can investigate in further what you are trying to build.
Since the goal is to have a headless experience, you may have seen that the content you get from WordPress (the content
data entry) is basically a long html string. When you are using Gutenberg as editor, you may have recognized that it uses blocks for content. Which is exactly what we need to render our own components for headlines, sections, etc.
This is where the WP GraphQL Gutenberg plugin we already installed comes in.
This allows you to write queries like:
query SinglePostBySlug($slug: String!) {
postBy(slug: $slug) {
id
title
blocks {
attributesJSON
name
innerBlocks {
attributesJSON
name
innerBlocks {
attributesJSON
name
innerBlocks {
attributesJSON
name
innerBlocks {
attributesJSON
name
}
}
}
}
}
date
slug
excerpt(format: RAW)
}
}
You now have your Gutenberg Blocks as a JSON schema, just like you would expect it from a real headless CMS. Nice!
From now on, what I did is created small richtext resolver components to display and loop through this JSON. Since this part is highly optionated and I only implemented what I need, the best is if you do the same.
Conclusion
It's actually easier than I thought to use WordPress as a Headless CMS. I am planning on building a Batteries Included Nuxt Layer to easily get started with WordPress Headless. In the meantime, some things that would be needed to be implemented:
Render the Gutenberg Blocks as Richtext Elements
Optimizing the images coming from WordPress with nuxt/image
Rendering both pages and posts (+ custom types) as nuxt pages.
Creating a sitemap
Add custom menus and query/render them.
Add SEO Meta tags (image, twitter, ...)
See also
You can install fuxt-backend, which is a theme to disable the regular website and enables some features like menus. Perfect for our usecase. fuxt is a nuxt 2 frontend for headless WordPress.
Questions, Issues, Suggestions?
If you need more help or have any questions, I can offer you a consulting session where we go through your code, analyze issues or build new features. Or do you need some custom Vue/Nuxt plugins, composables or modules specifically for your project? Feel free to contact me for more details, I would love to help you build great Nuxt applications.