WordPress, GraphQL & Gatsby
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
10up Lunch & Learn
Nov 2019
Who Am I?
- Software Engineer at Gatsby
- Denver Native
- WordPress developer for 10+ years
- I love WordPress, and Open Source in general
- Creator & maintainer of WPGraphQL
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4394624/IMG_1407.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/5785508/51295932_10101387968507503_2522186215629783040_n.jpg)
What We'll Go Over
- Why WordPress + GraphQL
- Installing and Using the WPGraphQL Plugin
- WordPress as an Application Data Graph
- Efficient Data Fetching with GraphQL
- WPGraphQL + Gatsby
- What's next?
Why WordPress + GraphQL?
Personal Story
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3773329/wpgraphql.png)
- Distributed WordPress Installs
- Content Syndication
- Micro-apps that can interact with any number of WordPress installs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3171601/laptop-90deg-themes.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260590/wp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260601/android.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761487/Image_uploaded_from_iOS.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053481/app-stpre.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053480/google-play.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3133290/wp-api_logo.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263382/printer.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
Frustrations with REST
- Implicit requests are difficult for long-term code maintenance
- Constant over/under-fetching
- Unnecessarily moving tons of data over the wire
- So many requests needed to READ / WRITE the data needed to syndicate
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3171601/laptop-90deg-themes.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260590/wp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260601/android.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761487/Image_uploaded_from_iOS.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053481/app-stpre.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053480/google-play.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3133290/wp-api_logo.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263382/printer.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3769533/spinner-2x.gif)
https://denverpost.com/wp-json/wp/v2/posts?per_page=100
Frustrations with REST
(still)
- Implicit requests are difficult for long-term code maintenance
- Constant over/under-fetching
- Unnecessarily moving tons of data over the wire
- So many requests needed to READ / WRITE the data needed to syndicate
-
Poor performace
- We wanted REST to provide everything so any client could have what they needed
- Wrapping a non-performant REST API with GraphQL === non-performant GraphQL API
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761473/dpsports.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761505/dp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3189478/StandardWordpressLaptop.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3171601/laptop-90deg-themes.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260590/wp-mobile.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4260601/android.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3761487/Image_uploaded_from_iOS.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053481/app-stpre.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4053480/google-play.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263382/printer.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3773329/wpgraphql.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3133290/wp-api_logo.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4236041/graphql-structure.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
WPGraphQL is a free, open-source WordPress plugin that provides an extendable GraphQL schema and API for any WordPress site.
Adding the Plugin to WordPress
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
- Visit Github: https://github.com/wp-graphql/wp-graphql
- Star the Repo
- Tweet about how awesome it is to be able to use GraphQL with WordPress
- Clone or Download the plugin
- Install & Activate
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263154/Screen_Shot_2017-10-25_at_10.07.27_AM.png)
Install & Activate the Plugin
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263245/Screen_Shot_2017-10-25_at_10.32.49_AM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4263290/Screen_Shot_2017-10-25_at_10.32.59_AM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4236041/graphql-structure.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4052454/512x512.png)
Posts
Pages
Users
Comments
Terms
Media
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3895856/wp-logo.png)
GraphQL
{
viewer {
name
}
}
{
data: {
viewer: {
name: "Jason Bahl"
}
}
}
GraphQL Query
GraphQL Result
Post
Category
Category
Category
Post
title
"Hello World"
title
"GoodBye Mars"
Image
Image
Image
name
"news"
name
"crime"
name
"sports"
Image
WordPress as an Application Data Graph
GraphQL
GraphQL lets us pick trees out of the Application Data Graph
GraphQL
query {
post(id: "cG9zdDox") {
title
link
categories {
nodes {
name
}
}
}
}
{
data: {
post: {
title: "Hello World!"
link: "http://site.com/hello-world"
categories: {
nodes: [
{
name: "sports"
},
{
name: "crime"
}
]
}
}
}
}
Post
Category
Category
Category
Post
title
"Hello World"
title
"GoodBye Mars"
Image
Image
Image
name
"news"
name
"crime"
name
"sports"
Image
Demo Time!
Comparing APIs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774770/Screen_Shot_2017-05-06_at_7.00.43_AM.png)
Comparing APIs
Data Requirements:
- List of posts
- author
- name
- avatar (size: 50)
- url
- site
- name
- link
- followLink
- title
- featuredImage (width: 300)
- url
- excerpt
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774781/excerpts.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774779/authors.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774783/follow.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774784/title.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774785/sites.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774782/featuredImages.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774780/edges.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774770/Screen_Shot_2017-05-06_at_7.00.43_AM.png)
Comparing APIs
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4388612/logo-php.png)
<?php
$posts = new WP_Query(
array(
'post_type' => 'post',
'posts_per_page' => 4,
'post_status' => 'publish'
)
);
# Loop through the posts
<?php
# Author Data
$author = get_the_author();
$author_name = $author->display_name;
$avatar = get_avatar_data( $author->ID );
$avatar_url = $avatar->url;
# Site Info
$sites = wp_get_post_terms(
$post->ID, 'sites'
);
$site_name = $sites[0]->name;
$site_link = get_term_meta(
$sites[0]->term_id,
'link',
true
);
# Follow Link
$follow_link = your_follow_link_func();
# Title
$title = get_the_title();
# Featured Image
$img = get_the_post_thumbnail_url(
$post->ID,
array( 300 )
);
# Excerpt
$excerpt = get_the_excerpt();
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774781/excerpts.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774779/authors.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774783/follow.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774784/title.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774785/sites.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774782/featuredImages.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774780/edges.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774770/Screen_Shot_2017-05-06_at_7.00.43_AM.png)
Comparing APIs
# Posts
# Assumes follow link is a registered
# field on the post endpoint
HTTP GET
/wp-json/wp/v2/posts?per_page=4
$posts.map((post) => {
$title = post.title;
$excerpt = post.excerpt;
$follow = post.followLink;
});
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774781/excerpts.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774779/authors.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774783/follow.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774784/title.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774785/sites.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774782/featuredImages.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774780/edges.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4388611/logo-js.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3133290/wp-api_logo.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4388874/logo-swift.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/4388883/logo-jav.png)
# Authors
HTTP GET
/wp-json/wp/v2/user/{posts[0].author}
/wp-json/wp/v2/user/{posts[1].author}
/wp-json/wp/v2/user/{posts[2].author}
/wp-json/wp/v2/user/{posts[3].author}
/wp-json/wp/v2/user/{posts[0].author}/avatar
/wp-json/wp/v2/user/{posts[1].author}/avatar
/wp-json/wp/v2/user/{posts[2].author}/avatar
/wp-json/wp/v2/user/{posts[3].author}/avatar
# Sites
# Assumes REST API was customized to show
# The "Sites Custom Taxonomy" IDs on the
# Post endpoint
HTTP GET
/wp-json/wp/v2/site/{posts[0].sites[0]}
/wp-json/wp/v2/site/{posts[1].sites[0]}
/wp-json/wp/v2/site/{posts[2].sites[0]}
/wp-json/wp/v2/site/{posts[3].sites[0]}
# Featured Images
HTTP GET
/wp-json/wp/v2/media/{posts[2].featured_media}
/wp-json/wp/v2/media/{posts[3].featured_media}
Comparing APIs
query {
posts(first: 4) {
edges {
node {
}
}
}
}
author {
name
avatar(size: 50) {
url
}
}
followLink
title
featuredImage(width: 300) {
url
}
excerpt
site {
name
link
}
Data Requirements:
- List of posts
- author
- name
- avatar (size: 50)
- url
- site
- name
- link
- followLink
- title
- featuredImage (width: 300)
- url
- excerpt
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774781/excerpts.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774779/authors.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774783/follow.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774784/title.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774785/sites.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774782/featuredImages.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774780/edges.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/3774770/Screen_Shot_2017-05-06_at_7.00.43_AM.png)
Efficient Data Fetching
Solving the N+1 Problem
WPGraphQL Roadmap & v1.0
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/6774887/Screen_Shot_2019-11-14_at_9.05.48_AM.png)
WPGraphQL Roadmap & v1.0
- Address any predictable breaking changes
- Nesting 1-to-1 Connections
- Revisions / Preview Support
- Better Interface implementation/support
- Content connection (Query posts of any Post Type)
- Security Audit
Gatsby Source WPGraphQL
![](https://s3.amazonaws.com/media-p.slid.es/uploads/340215/images/6774973/Screen_Shot_2019-11-14_at_9.25.32_AM.png)
Enable you to do the same things you do with WordPress today, but better
Gatsby Source WPGraphQL
- Deprecate Gatsby Source WordPress (WP REST API)
- Template Hierarchy
- Routing
- Search
- Preview
- Efficient Caching (fast rebuilds)
- Live Data Updates (Instant rebuilds)*
WordPress, GraphQL & Gatsby - 10up
By Jason Bahl
WordPress, GraphQL & Gatsby - 10up
GraphQL for WordPress
- 1,154