Evolving WordPress
with GraphQL
WordCamp US 2017
Jason Bahl | @jasonbahl
https://slides.com/jasonbahl-1/wcus-wordpress-graphql
Me.
- Senior WordPress Engineer at Digital First Media in Denver, CO
- Denver native
- WordPress Developer for 9+ years
- I love WordPress, and Open Source in general
- Creator & maintainer of WPGraphQL
This Talk.
- Evolution of WordPress as a platform
- Various ways to interact with WordPress
- PHP (plugins/themes)
- REST API (decoupled environments)
- Observations and Pain Points
- Intro to GraphQL
- What is GraphQL?
- What problems it solves & how
- Using GraphQL with WordPress
- QueriesÂ
- Mutations
- GraphQL Extensions & Tooling
Evolution of WordPress
Working with WordPress Data
Working with WordPress Data
Data Requirements:
- List of posts
- author
- name
- avatar (size: 50)
- url
- site
- name
- link
- followLink
- title
- featuredImage (width: 300)
- url
- excerpt
Working with WordPress Data
<?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();
Working with WordPress Data
# 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;
});
# 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}
Working with WordPress Data
Observations / Pain Points
- Completely different experience using PHP and REST
- REST: Endpoint requests are implicit
- REST: Requires a LOT of HTTP requests
- REST: Constant simultaneous over/under fetching
- REST: Lots of endpoints
- REST: Client is tightly coupled to the server
Working with WordPress Data
Observed Pain Points
Completely different experience using PHP and REST
<?php
# Author Data
if ( $posts->have_posts() ) {
while ( $posts->have_posts() ) {
$posts->the_post();
$author = get_the_author();
$author_name = $author->display_name;
$avatar = get_avatar_data(
$author->ID
);
$avatar_url = $avatar->url;
}
}
# Author Data
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
Working with WordPress Data
Observed Pain Points
REST: Endpoint requests are implicit
# Author Data
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
- Difficult to understand intent of original code months later
- What fields and data are expected to be returned by what endpoint?
- Constantly need to refer to docs, which often don't exist
Working with WordPress Data
Observed Pain Points
REST: Requires a LOT of HTTP requests
# Author Data
HTTP GET
/wp-json/wp/v2/posts?per_page=4
/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
/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]}
/wp-json/wp/v2/media/{posts[2].featured_media}
/wp-json/wp/v2/media/{posts[3].featured_media}
- 15 HTTP requests!
- Poor performing clients
- Hard to manage code to relate it all
- Hard to debug
Working with WordPress Data
Observed Pain Points
REST: Constant simultaneous over/under fetching
- Each endpoint provides more fields than we needed
- Each endpoint provides less fields than we needed
- Leads to excessive round trip requests
- Leads to excess, unnecessary bandwidth
- performance and cost considerations, specifically for mobile
Working with WordPress Data
Observed Pain Points
REST: Lots of endpoints
- Server: Difficult to maintain, version and document
- Client: Difficult to keep up with what endpoint does what, especially if documentation is poorly maintained
- Difficult to wire up code to make connections between endpoints
# Author Data
HTTP GET
/wp-json/wp/v2/posts?per_page=4
/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
/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]}
/wp-json/wp/v2/media/{posts[2].featured_media}
/wp-json/wp/v2/media/{posts[3].featured_media}
Working with WordPress Data
Observed Pain Points
REST: Client is tightly coupled to the server
- Server controls the shape of the data
- Server can't know what fields clients are using
- Hard to version/deprecate fields in endpoints
- Leads to versioning entire endpoints
- Leads to endpoint bloat, typically poor documentation, duplicate code/tech debt.
- Completely different experience using PHP and REST
- REST: Endpoint requests are implicit
- REST: Requires a LOT of HTTP requests
- REST: Constant simultaneous over/under fetching
- REST: Lots of endpoints
- REST: Client is tightly coupled to the server
- Query the same way from PHP and remote HTTP clients
- Explicit requests, more readable/maintainable code
- Fetch multiple resources in a single request
- Fetch exactly what you need, nothing more
- Single, self-documenting endpoint
- Decoupled client-server relationship
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools
GraphQL
A Query Language for your API
GraphQL
Graph != Graph Databases
Graph != Graph Search
Graph = Application Data Graph
QL = Query Language
GraphQL: A Query Language for Your Application Data Graph
Post
Category
Category
Category
Post
title
"Hello World"
title
"GoodBye Mars"
Image
Image
Image
name
"news"
name
"crime"
name
"sports"
Image
GraphQL
GraphQL lets us pick trees out of the Application Data Graph
GraphQL
query {
post(id: "...") {
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
Why GraphQL?
Completely different experience using PHP and REST
# WordPress, PHP with WPGraphQL Active
$query = '
{
posts {
edges {
node {
id
title
link
}
}
}
}
';
$data = do_graphql_request( $query );
# JavaScript, using Fetch
const query = `{
posts {
edges {
node {
id
title
link
}
}
}
}
`;
fetch('site.com/graphql', {
method: "POST",
body: {
query: query
}
});
Similar experience using GraphQL in PHP or any other language
<?php
# Author Data
if ( $posts->have_posts() ) {
while ( $posts->have_posts() ) {
$posts->the_post();
$author = get_the_author();
$author_name = $author->display_name;
$avatar = get_avatar_data(
$author->ID
);
$avatar_url = $avatar->url;
}
}
# Author Data
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
# CURL
$ curl -XPOST
-H "Content-Type:application"
-d '{"query":"{
posts{
edges{
node{
id
title
link
}
}
}
}"}'
http://site.com/graphql
Why GraphQL?
Completely different experience using PHP and REST
# WordPress, PHP with WPGraphQL Active
$query = '
{
posts {
edges {
node {
id
title
link
}
}
}
}
';
$data = do_graphql_request( $query );
# JavaScript, using Fetch
const query = `{
posts {
edges {
node {
id
title
link
}
}
}
}
`;
fetch('site.com/graphql', {
method: "POST",
body: {
query: query
}
});
Similar experience using GraphQL in PHP or any other language
<?php
# Author Data
if ( $posts->have_posts() ) {
while ( $posts->have_posts() ) {
$posts->the_post();
$author = get_the_author();
$author_name = $author->display_name;
$avatar = get_avatar_data(
$author->ID
);
$avatar_url = $avatar->url;
}
}
# Author Data
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
# CURL
$ curl -XPOST
-H "Content-Type:application"
-d '{"query":"{
posts{
edges{
node{
id
title
link
}
}
}
}"}'
http://site.com/graphql
OLD: Draft for WCUS
By Jason Bahl
OLD: Draft for WCUS
- 1,617