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,438