DFMguest | DFMvisitor
• Local WordPress Install
• WPGraphQL plugin, activated, permalinks flushed
• WPGraphiQL plugin activated (or GraphiQL Desktop App)
• Code / Text Editor
With the WP REST API and WPGraphQL
With the WP REST API and WPGraphQL
WordCamp for Publishers 2017
Jason Bahl | @jasonbahl
Powered by the WP REST API
Content originates in one system and is pushed to other systems
Content originates in an external system and is "pulled" into the user's system.
{
"id": 123,
"date": "2017-08-09T13:39:18",
"date_gmt": "2017-08-09T18:39:18",
"guid": {
"rendered": "http://www.denverpost.com/?p=123"
},
"modified": "2017-08-10T17:16:57",
"modified_gmt": "2017-08-10T22:16:57",
"slug": "article-title",
"status": "publish",
"type": "post",
"link": "https://poststatus.com/article-title/",
"title": {
"rendered": "Article Title"
},
"content": {
"rendered": "Some content",
},
...
}
Push / Pull Syndication at DFM
Articles syndicated across 9 WordPress sites since
Feb 2016 - May 2017
*not including article updates, media or other connected entities
Articles syndicated each day
Content is fetched from remote system and displayed by the client
The content is only represented by the client system, not duplicated in another data store
{
"id": 123,
"date": "2017-08-09T13:39:18",
"date_gmt": "2017-08-09T18:39:18",
"guid": {
"rendered": "http://www.denverpost.com/?p=123"
},
"modified": "2017-08-10T17:16:57",
"modified_gmt": "2017-08-10T22:16:57",
"slug": "article-title",
"status": "publish",
"type": "post",
"link": "https://poststatus.com/article-title/",
"title": {
"rendered": "Article Title"
},
"content": {
"rendered": "Some content",
},
...
}
About Syndicating Content with REST
About Syndicating Content with REST
5,207 bytes
4,725 bytes
5,823 bytes
{
"data": {
"articles": [
{
"author": {
"avatar": {
"url": "http://gravatar.com/?id=123&w=50"
},
"name": "Hillary",
"link": "http://hillarys-site.com",
"followLink": "http://example.com/?follow_author=1"
},
"title": "The Bookshop, by Penelope Fitzgerald",
"link": "http://example.com/article-path",
"excerpt": "I am very much enjoying...",
"publication": {
"name": "Vulpes Libris",
"link": "http://example-publication.com"
}
}
]
}
}
GET /posts
{
"id": 38424,
"date": "2017-08-09T13:39:18",
"date_gmt": "2017-08-09T18:39:18",
"guid": {
"rendered": "https://example.com/?p=38424"
},
"modified": "2017-08-10T17:16:57",
"modified_gmt": "2017-08-10T22:16:57",
"slug": "publish-conference-pictures",
"status": "publish",
"type": "post",
"link": "https://example.com/publish-conference-pictures/",
"title": {
"rendered": "The Bookshop, by Penelope Fitzgerald"
},
"content": {
"rendered": "<p>I am very much enjoying...Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.</p>\n<p>Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.</p>\n<p>Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.</p>\n<p>
Podcasting operational change management inside of workflows to establish a framework. Taking seamless key performance indicators offline to maximise the long tail. Keeping your eye on the ball while performing a deep dive on the start-up mentality to derive convergence on cross-platform integration.</p>\n<p>Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI.</p>\n<p>Efficiently unleash cross-media information without cross-media value. Quickly maximize timely deliverables for real-time schemas. Dramatically maintain clicks-and-mortar solutions without functional solutions.</p>\n<p>Completely synergize resource taxing relationships via premier niche markets. Professionally cultivate one-to-one customer service with robust ideas. Dynamically innovate resource-leveling customer service for state of the art customer service.</p>\n<p>Objectively innovate empowered manufactured products whereas parallel platforms. Holisticly predominate extensible testing procedures for reliable supply chains. Dramatically engage top-line web services vis-a-vis cutting-edge deliverables.</p><p>Proactively envisioned multimedia based expertise and cross-media growth strategies. Seamlessly visualize quality intellectual capital without superior collaboration and idea-sharing. Holistically pontificate installed base portals after maintainable products.</p><p>Phosfluorescently engage worldwide methodologies with web-enabled technology. Interactively coordinate proactive e-commerce via process-centric "outside the box" thinking. Completely pursue scalable customer service through sustainable potentialities.</p>",
"protected": false
},
"excerpt": {
"rendered": "<p>I am very much enjoying...Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.</p>\n",
"protected": false
},
"author": 1,
"featured_media": 38425,
"comment_status": "open",
"ping_status": "closed",
"sticky": false,
"template": "",
"format": "standard",
"meta": [
],
"categories": [
6,
196
],
"tags": [
178
],
"_links": {
"self": [
{
"href": "https://example.com/wp-json/wp/v2/posts/38424"
}
],
"collection": [
{
"href": "https://example.com/wp-json/wp/v2/posts"
}
],
"about": [
{
"href": "https://example.com/wp-json/wp/v2/types/post"
}
],
"author": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/users/1"
}
],
"replies": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/comments?post=38424"
}
],
"version-history": [
{
"href": "https://example.com/wp-json/wp/v2/posts/38424/revisions"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/media/38425"
}
],
"wp:attachment": [
{
"href": "https://example.com/wp-json/wp/v2/media?parent=38424"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/categories?post=38424"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/tags?post=38424"
},
{
"taxonomy": "yst_prominent_words",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/yst_prominent_words?post=38424"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
}
GET /posts foreach post: GET /post/:id/:author GET /author/:id/avatar GET /post/:id/publication
A Query Language for your API
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
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
query {
post(id: "...") {
title
link
coauthors {
name
}
}
}
{
data: {
post: {
title: "Hello World!"
link: "http://site.com/hello-world"
coauthors: [
{
name: "John Doe",
},
{
name: "Jane Smith",
}
]
}
}
}
Post
CoAuthor
CoAuthor
CoAuthor
Post
Featured
Image
title
"Hello World"
title
"GoodBye Mars"
Avatar
Avatar
Avatar
name
"jane doe"
name
"sue smith"
name
"john doe"
GET /post/:id
{
"id": 38424,
"date": "2017-08-09T13:39:18",
"date_gmt": "2017-08-09T18:39:18",
"guid": {
"rendered": "https://example.com/?p=38424"
},
"modified": "2017-08-10T17:16:57",
"modified_gmt": "2017-08-10T22:16:57",
"slug": "publish-conference-pictures",
"status": "publish",
"type": "post",
"link": "https://example.com/publish-conference-pictures/",
"title": {
"rendered": "The Bookshop, by Penelope Fitzgerald"
},
"content": {
"rendered": "<p>I am very much enjoying...Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.</p>\n<p>Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.</p>\n<p>Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.</p>\n<p>
Podcasting operational change management inside of workflows to establish a framework. Taking seamless key performance indicators offline to maximise the long tail. Keeping your eye on the ball while performing a deep dive on the start-up mentality to derive convergence on cross-platform integration.</p>\n<p>Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI.</p>\n<p>Efficiently unleash cross-media information without cross-media value. Quickly maximize timely deliverables for real-time schemas. Dramatically maintain clicks-and-mortar solutions without functional solutions.</p>\n<p>Completely synergize resource taxing relationships via premier niche markets. Professionally cultivate one-to-one customer service with robust ideas. Dynamically innovate resource-leveling customer service for state of the art customer service.</p>\n<p>Objectively innovate empowered manufactured products whereas parallel platforms. Holisticly predominate extensible testing procedures for reliable supply chains. Dramatically engage top-line web services vis-a-vis cutting-edge deliverables.</p><p>Proactively envisioned multimedia based expertise and cross-media growth strategies. Seamlessly visualize quality intellectual capital without superior collaboration and idea-sharing. Holistically pontificate installed base portals after maintainable products.</p><p>Phosfluorescently engage worldwide methodologies with web-enabled technology. Interactively coordinate proactive e-commerce via process-centric "outside the box" thinking. Completely pursue scalable customer service through sustainable potentialities.</p>",
"protected": false
},
"excerpt": {
"rendered": "<p>I am very much enjoying...Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.</p>\n",
"protected": false
},
"author": 1,
"featured_media": 38425,
"comment_status": "open",
"ping_status": "closed",
"sticky": false,
"template": "",
"format": "standard",
"meta": [
],
"categories": [
6,
196
],
"tags": [
178
],
"_links": {
"self": [
{
"href": "https://example.com/wp-json/wp/v2/posts/38424"
}
],
"collection": [
{
"href": "https://example.com/wp-json/wp/v2/posts"
}
],
"about": [
{
"href": "https://example.com/wp-json/wp/v2/types/post"
}
],
"author": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/users/1"
}
],
"replies": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/comments?post=38424"
}
],
"version-history": [
{
"href": "https://example.com/wp-json/wp/v2/posts/38424/revisions"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/media/38425"
}
],
"wp:attachment": [
{
"href": "https://example.com/wp-json/wp/v2/media?parent=38424"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/categories?post=38424"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/tags?post=38424"
},
{
"taxonomy": "yst_prominent_words",
"embeddable": true,
"href": "https://example.com/wp-json/wp/v2/yst_prominent_words?post=38424"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
}
GET /users/1
{
"id": 1,
"name": "Jane Doe",
"url": "https://example.com",
"description": "I'm a WordPress developer from Birmingham, Alabama. I'm the creator and editor of Post Status.",
"link": "https://poststatus.com/profiles/brian-krogsgard/",
"slug": "jane-doe",
"avatar_urls": {
"24": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=24&d=mm&r=g",
"48": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=48&d=mm&r=g",
"96": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=96&d=mm&r=g"
},
"meta": [
],
"_links": {
"self": [
{
"href": "https://poststatus.com/wp-json/wp/v2/users/1"
}
],
"collection": [
{
"href": "https://poststatus.com/wp-json/wp/v2/users"
}
]
}
}
GET /users/2
{
"id": 2,
"name": "John Doe",
"url": "https://example.com",
"description": "I'm a WordPress developer from Birmingham, Alabama. I'm the creator and editor of Post Status.",
"link": "https://poststatus.com/profiles/brian-krogsgard/",
"slug": "john-doe",
"avatar_urls": {
"24": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=24&d=mm&r=g",
"48": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=48&d=mm&r=g",
"96": "https://secure.gravatar.com/avatar/6b89515a9781282ae3a66d4b6173523c?s=96&d=mm&r=g"
},
"meta": [
],
"_links": {
"self": [
{
"href": "https://poststatus.com/wp-json/wp/v2/users/1"
}
],
"collection": [
{
"href": "https://poststatus.com/wp-json/wp/v2/users"
}
]
}
}
5,460 bytes
819 bytes
819 bytes
(sample payloads from poststatus.com)
query {
post(id: "...") {
title
link
coauthors {
name
}
}
}
{
data: {
post: {
title: "Hello World!"
link: "http://site.com/hello-world"
coauthors: [
{
name: "John Doe",
},
{
name: "Jane Smith",
}
]
}
}
}
231 bytes
query {
posts {
edges {
node {
}
}
}
}
author {
name
avatar(size: 50) {
url
}
}
followLink
title
featuredImage(width: 300) {
url
}
excerpt
site {
name
link
}
A Query Language for your WordPress API
A Query Language for your API
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
WPGraphQL is an extendable open-source WordPress plugin that brings the power of GraphQL to your WordPress install. WPGraphQL defines GraphQL Types for core WordPress entities, such as posts, terms and users, and defines core entry points into the WordPress Application Data Graph via GraphQL Queries and Mutations. WPGraphQL gives clients the power to ask for exactly what they need from WordPress and enables powerful developer tools.
https://github.com/wp-graphql/wp-graphiql
Download, install & activate plugin, navigate to GraphiQL in the WordPress Dashboard
explore GraphQL Documentation
query a list of posts
query nested resources
query multiple root resources
using the node query
using query fragments with GraphQL
using aliases with GraphQL
use variables with GraphQL
queries with variables and pagination
mutation - createPost
GraphQL Docs
IDE Support
Let's build a plugin!
<?php
/**
* Plugin Name: WPGraphQL WordCamp Publishers Extension
* Description: Plugin that extends WPGraphQL
*/
Add a Root Entry Point
Add a Root Entry Point
add_action( 'graphql_root_queries', function( $fields ) {
$fields['wordCampRocks'] = [
'type' => \WPGraphQL\Types::string(),
'description' => __( 'An example field showing how to add
to the root schema', 'wp-graphql-publishers' ),
'resolve' => function() {
return 'Yes, it does';
},
];
return $fields;
} );
Add a Field to the Post Schema
Add a Field to the Post Schema
add_action( 'graphql_post_fields', function( $fields ) {
$fields['color'] = [
'type' => \WPGraphQL\Types::string(),
'description' => __( 'An example showing how to add a
field to the "post" schema', 'wp-graphql-publishers' ),
'resolve' => function( \WP_Post $post ) {
return get_post_meta( $post->ID, 'color', true );
},
];
return $fields;
}, 10, 1);
Add Custom Post Type to the Schema
Add Custom Post Type to the Schema
add_action( 'init', function() {
register_post_type( 'book', [
'label' => __( 'Books', 'wp-graphql-publishers' ),
'supports' => [ 'title', 'editor', 'custom-fields' ],
'public' => true,
'show_in_graphql' => true,
'graphql_single_name' => 'book',
'graphql_plural_name' => 'books',
] );
} );
Add Custom Taxonomy to the Schema
Add Custom Taxonomy to the Schema
add_action( 'init', function() {
register_taxonomy( 'genre', 'book', [
'label' => __( 'Genre' ),
'public' => true,
'show_in_graphql' => true,
'graphql_single_name' => 'genre',
'graphql_plural_name' => 'genres',
'hierarchical' => true,
]);
} );
Add a Custom Field with Mutation Support
Add a Custom Field with Mutation Support
add_action( 'graphql_book_fields', function( $fields ) {
$fields['price'] = [
'type' => \WPGraphQL\Types::string(),
'description' => __( 'The price of the book', 'wp-graphql-publishers' ),
'resolve' => function( \WP_Post $book ) {
$price = get_post_meta( $book->ID, 'price', true );
return ! empty( $price ) ? $price : null;
},
];
return $fields;
}, 10, 1);
1. Add the field to the type
Add a Custom Field with Mutation Support
add_action( 'graphql_post_object_mutation_input_fields', function( $fields, \WP_Post_Type $post_type_object ) {
if ( 'book' === $post_type_object->name ) {
$fields['price'] = [
'type' => \WPGraphQL\Types::string(),
'description' => __( 'The price of the book', 'wp-graphql-publishers' ),
];
}
return $fields;
}, 10, 2 );
2. Add the input field to the mutation input
Add a Custom Field with Mutation Support
add_action( 'graphql_post_object_mutation_update_additional_data', 'prefix_update_price', 10, 3 );
function prefix_update_price( $post_id, $input, \WP_Post_Type $post_type_object ) {
if ( 'book' === $post_type_object->name && ! empty( $input['price'] ) ) {
update_post_meta( $post_id, 'price', $input['price'] );
}
}
3. Add the input field to the mutation input
Add a Custom Field with Mutation Support
add_action( 'graphql_post_object_mutation_input_fields', function( $fields, \WP_Post_Type $post_type_object ) {
if ( 'book' === $post_type_object->name ) {
$fields['price'] = [
'type' => \WPGraphQL\Types::string(),
'description' => __( 'The price of the book', 'wp-graphql-publishers' ),
];
}
return $fields;
}, 10, 2 );
2. Add the input type