Category: Video

  • Query any page by its path using WPGraphQL

    One of the most common ways WordPress is used, is by users visiting a URL of a WordPress site and reading the content on the page.

    WordPress has internal mechanisms that take the url from the request, determine what type of entity the user is requesting (a page, a blog post, a taxonomy term archive, an author’s page, etc) and then returns a specific template for that type of content.

    This is a convention that users experience daily on the web, and something developers use to deliver unique experiences for their website users.

    When you go “headless” with WordPress, and use something other than WordPress’s native theme layer to display the content, it can be tricky to determine how to take a url provided by a user and convert that into content to show your users.

    In this post, we’ll take a look at a powerful feature of WPGraphQL, the nodeByUri query, which accepts a uri input (the path to the resource) and will return the node (the WordPress entity) in response.

    You can use this to re-create the same experience WordPress theme layer provides, by returning unique templates based on the type of content being requested.

    WPGraphQL’s “nodeByUri” query

    One of the benefits of GraphQL is that it can provide entry points into the “graph” that (using Interfaces or Unions) can return different Types of data from one field.

    WPGraphQL provides a field at the root of the graph named nodeByUri. This field accepts one argument as input, a $uri. And it returns a node, of any Type that has a uri. This means any public entity in WordPress, such as published authors, archive pages, posts of a public post type, terms of a public taxonomy, etc.

    When a URI is input, this field resolves to the “node” (post, page, etc) that is associated with the URI, much like entering the URI in a web browser would resolve to that piece of content.

    If you’ve not already used the “nodeByUri” query, it might be difficult to understand just reading about it, so let’s take a look at this in action.

    Here’s a video where I walk through it, and below are some highlights of what I show in the video.

    Video showing how to use the nodeByUri query in WPGraphQL

    Writing the query

    Let’s start by querying the homepage.

    First, we’ll write our query:

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
      }
    }

    In this query, we’re doing a few things.

    First, we give our query a name “GetNodeByUri”. This name can be anything we want, but it can be helpful with tooling, so it’s best practice to give your queries good names.

    Next, we define our variable input to accept: $uri: String!. This tells GraphQL that there will be one input that we don’t know about up front, but we agree that we will submit the input as a string.

    Next, we declare what field we want to access in the graph: nodeByUri( uri: $uri ). We’re telling WPGraphQL that we want to give it a URI, and in response, we want a node back.

    The nodeByUri field is defined in the Schema to return the GraphQL Type UniformResourceIdentifiable, which is a GraphQL Interface implemented by any Type in the Graph that can be accessed via a public uri.

    Screenshot of the nodeByUri field shown in GraphiQL

    If we inspect the documentation in GraphiQL for this type, we can see all of the available Types that can be returned.

    Screenshot of the UniformResourceIdentifiable GraphQL Interface in GraphiQL.

    The Types that can be returned consist of public Post Types, Public Taxonomies, ContentType (archives), MediaItem, and User (published authors are public).

    So, we know that any uri (path) that we query, we know what we can ask for and what to expect in response.

    Execute the query

    Now that we have the query written, we can use GraphiQL to execute the query.

    GraphiQL has a “variables” pane that we will use to input our variables. In this case, the “uri” (or path) to the resource is our variable.

    First, we will enter “/” as our uri value so we can test querying the home page.

    Screenshot of the “uri” variable entered in the GraphiQL Variables pane.

    Now, we can execute our query by pressing the “Play” button in GraphiQL.

    And in response we should see the following response:

    {
      "data": {
        "nodeByUri": {
          "__typename": "ContentType"
        }
      }
    }
    Screenshot of the nodeByUri query for the “/” uri.

    Expanding the query

    We can see that when we query for the home page, we’re getting a “ContentType” node in response.

    We can expand the query to ask for more fields of the “ContentType”.

    If we look at the home page of https://demo.wpgraphql.com, we will see that it serves as the “blogroll” or the blog index. It’s a list of blog posts.

    This is why WPGraphQL returns a “ContentType” node from the Graph.

    We can write a fragment on this Type to ask for fields we want when the query returns a “ContentType” node.

    If we look at the documentation in GraphiQL for the ContentType type, we can see all the fields that we can ask for.

    Screenshot of the ContentType documentation in GraphiQL

    If our goal is to re-create the homepage we’re seeing in WordPress, then we certainly don’t need all the fields! We can specify exactly what we need.

    In this case, we want to ask for the following fields:

    • name: the name of the content type
    • isFrontPage: whether the contentType should be considered the front page
    • contentNodes (and sub-fields): a connection to the contentNodes on the page

    This should give us enough information to re-create what we’re seeing on the homepage.

    Let’s update our query to the following:

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ... on ContentType {
          name
          uri
          isFrontPage
          contentNodes {
            nodes {
              __typename
              ... on Post {
                id
                title
              }
            }
          }
        }
      }
    }
    

    And then execute the query again.

    We now see the following results:

    {
      "data": {
        "nodeByUri": {
          "__typename": "ContentType",
          "name": "post",
          "uri": "/",
          "isFrontPage": true,
          "contentNodes": {
            "nodes": [
              {
                "__typename": "Post",
                "id": "cG9zdDoxMDMx",
                "title": "Tiled Gallery"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDoxMDI3",
                "title": "Twitter Embeds"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDoxMDE2",
                "title": "Featured Image (Vertical)…yo"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDoxMDEx",
                "title": "Featured Image (Horizontal)…yo"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDoxMDAw",
                "title": "Nested And Mixed Lists"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDo5OTY=",
                "title": "More Tag"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDo5OTM=",
                "title": "Excerpt"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDo5MTk=",
                "title": "Markup And Formatting"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDo5MDM=",
                "title": "Image Alignment"
              },
              {
                "__typename": "Post",
                "id": "cG9zdDo4OTU=",
                "title": "Text Alignment"
              }
            ]
          }
        }
      }
    }

    If we compare these results from our GraphQL Query, we can see that we’re starting to get data that matches the homepage that WordPress is rendering.

    Screenshot of the homepage

    There’s more information on each post, such as:

    • post author
      • name
      • avatar url
    • post date
    • post content
    • uri (to link to the post with)

    We can update our query once more with this additional information.

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ... on ContentType {
          name
          uri
          isFrontPage
          contentNodes {
            nodes {
              __typename
              ... on Post {
                id
                title
                author {
                  node {
                    name
                    avatar {
                      url
                    }
                  }
                }
                date
                content
                uri
              }
            }
          }
        }
      }
    }

    Breaking into Fragments

    The query is now getting us all the information we need, but it’s starting to get a bit long.

    We can use a feature of GraphQL called Fragments to break this into smaller pieces.

    I’ve broken the query into several Fragments:

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ...ContentType
      }
    }
    
    fragment ContentType on ContentType {
      name
      uri
      isFrontPage
      contentNodes {
        nodes {
          ...Post
        }
      }
    }
    
    fragment Post on Post {
      __typename
      id
      date
      uri
      content
      title
      ...Author
    }
    
    fragment Author on NodeWithAuthor {
      author {
        node {
          name
          avatar {
            url
          }
        }
      }
    }
    

    Fragments allow us to break the query into smaller pieces, and the fragments can ultimately be coupled with their components that need the data being asked for in the fragment.

    Here, I’ve created 3 named fragments:

    • ContentType
    • Post
    • Author

    And then we’ve reduced the nodeByUri field to only ask for 2 fields:

    • __typename
    • uri

    The primary responsibility of the nodeByUri field is to get the node and return it to us with the __typename of the node.

    The ContentType fragment is now responsible for declaring what is important if the node is of the ContentType type.

    The responsibility of this Fragment is to get some details about the type, then get the content nodes (posts) associated with it. It’s not concerned with the details of the post, though, so that becomes another fragment.

    The Post fragment defines the fields needed to render each post, then uses one last Author fragment to get the details of the post author.

    We can execute this query, and get all the data we need to re-create the homepage!! (sidebar widgets not included)

    Querying a Page

    Now, we can expand our query to account for different types.

    If we enter the /about path into our “uri” variable, and execute the same query, we will get this payload:

    {
      "data": {
        "nodeByUri": {
          "__typename": "Page"
        }
      }
    }
    Screenshot of initial query for the “/about” uri

    We’re only getting the __typename field in response, because we’ve told GraphQL to only return data ...on ContentType and since the node was not of the ContentType type, we’re not getting any data.

    Writing the fragment

    So now, we can write a fragment to ask for the specific information we need if the type is a Page.

    fragment Page on Page {
      title
      content
      commentCount
      comments {
        nodes {
          id
          content
          date
          author {
            node {
              id
              name
              ... on User {
                avatar {
                  url
                }
              }
            }
          }
        }
      }
    }

    And we can work that into the `nodeByUri` query like so:

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ...ContentType
        ...Page
      }
    }

    So our full query document becomes (and we could break the comments of the page into fragments as well, too):

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ...ContentType
        ...Page
      }
    }
    
    fragment Page on Page {
      title
      content
      commentCount
      comments {
        nodes {
          id
          content
          date
          author {
            node {
              id
              name
              ... on User {
                avatar {
                  url
                }
              }
            }
          }
        }
      }
    }
    
    fragment ContentType on ContentType {
      name
      uri
      isFrontPage
      contentNodes {
        nodes {
          ...Post
        }
      }
    }
    
    fragment Post on Post {
      __typename
      id
      date
      uri
      content
      title
      ...Author
    }
    
    fragment Author on NodeWithAuthor {
      author {
        node {
          name
          avatar {
            url
          }
        }
      }
    }
    

    And when we execute the query for the “/about” page now, we are getting enough information again, to reproduce the page that WordPress renders:

    {
      "data": {
        "nodeByUri": {
          "__typename": "Page",
          "title": "About",
          "content": "

    WP Test is a fantastically exhaustive set of test data to measure the integrity of your plugins and themes.

    \n

    The foundation of these tests are derived from WordPress’ Theme Unit Test Codex data. It’s paired with lessons learned from over three years of theme and plugin support, and baffling corner cases, to create a potent cocktail of simulated, quirky user content.

    \n

    The word “comprehensive” was purposely left off this description. It’s not. There will always be something new squarely scenario to test. That’s where you come in. Let us know of a test we’re not covering. We’d love to squash it.

    \n

    Let’s make WordPress testing easier and resilient together.

    \n", "commentCount": 1, "comments": { "nodes": [ { "id": "Y29tbWVudDo1NjUy", "content": "

    Test comment

    \n", "date": "2021-12-22 12:07:54", "author": { "node": { "id": "dXNlcjoy", "name": "wpgraphqldemo", "avatar": { "url": "https://secure.gravatar.com/avatar/94bf4ea789246f76c48bcf8509bcf01e?s=96&d=mm&r=g" } } } } ] } } } }

    Querying a Category Archive

    We’ve looked at querying the home page and a regular page, so now let’s look at querying a category archive page.

    If we navigate to https://demo.wpgraphql.com/category/alignment/, we’ll see that it’s the archive page for the “Alignment” category. It displays posts of the category.

    Screenshot of the Alignment category page rendered by WordPress

    If we add “/category/alignment” as our variable input to the query, we’ll now get the following response:

    {
      "data": {
        "nodeByUri": {
          "__typename": "Category"
        }
      }
    }
    Screenshot of querying the “alignment” category in GraphiQL

    So now we can write our fragment for what data we want returned when the response type is “Category”:

    Looking at the template we want to re-create, we know we need to ask for:

    • Category Name
    • Category Description
    • Posts of that category
      • title
      • content
      • author
        • name
        • avatar url
      • categories
        • name
        • uri

    So we can write a fragment like so:

    fragment Category on Category {
      name
      description
      posts {
        nodes {
          id
          title
          content
          author {
            node {
              name
              avatar {
                url
              }
            }
          }
          categories {
            nodes {
              name
              uri
            }
          }
        }
      }
    }

    And now our full query document looks like so:

    query GetNodeByUri($uri: String!) {
      nodeByUri(uri: $uri) {
        __typename
        ...ContentType
        ...Page
        ...Category
      }
    }
    
    fragment Category on Category {
      name
      description
      posts {
        nodes {
          id
          title
          content
          author {
            node {
              name
              avatar {
                url
              }
            }
          }
          categories {
            nodes {
              name
              uri
            }
          }
        }
      }
    }
    
    fragment Page on Page {
      title
      content
      commentCount
      comments {
        nodes {
          id
          content
          date
          author {
            node {
              id
              name
              ... on User {
                avatar {
                  url
                }
              }
            }
          }
        }
      }
    }
    
    fragment ContentType on ContentType {
      name
      uri
      isFrontPage
      contentNodes {
        nodes {
          ...Post
        }
      }
    }
    
    fragment Post on Post {
      __typename
      id
      date
      uri
      content
      title
      ...Author
    }
    
    fragment Author on NodeWithAuthor {
      author {
        node {
          name
          avatar {
            url
          }
        }
      }
    }

    And when I execute the query for the category, I get all the data I need to create the category archive page.

    Amazing!

    Any Type that can be returned by the nodeByUri field can be turned into a fragment, which can then be coupled with the Component that will render the data.

  • WPGraphQL featured on the Matt Report

    On February 28, The Matt Report podcast published the episode where Matt and I discussed WPGraphQL and WordPress.

    In the interview we discussed topics such as using WordPress with Gatsby, Gutenberg, Component based architecture, and more.

    Check it out over at MattReport.com, or catch the video interview on YouTube:

  • WPGraphQL featured on Torque News Drop

    I had the pleasure of joining Doc Pop Roberts on the Torque Magazine News Drop. We spent 18 minutes discussing headless WordPress and WPGraphQL.

    Give it a watch!!

    https://www.youtube.com/watch?v=8SAdtU8HAwM&feature=youtu.be
  • Registering Types and Fields: Pair Programming with Jacob Arriola

    On Thursday, March 26, 2020 I pair programmed with Jacob Arriola, Senior Web Engineer at Zeek Interactive.

    Zeek Interactive is a California-based software agency specializing in WordPress and Apple iOS. They recently moved their own marketing website from traditional WordPress to Headless WordPress with WPGraphQL as the API and Gatsby as the front-end.

    With the recent move of their site to Gatsby, and more Gatsby + WordPress projects in the pipeline, Jacob wanted to get more familiar with extending the WPGraphQL Schema.

    In this video, we walk through registering custom Types and Fields to the WPGraphQL Schema. We use the popular WordPress SEO by Yoast plugin as an example of custom data we’d like to expose to the WPGraphQL Schema.

    NOTE: There is an existing WPGraphQL + WordPress SEO by Yoast extension here: https://github.com/ashhitch/wp-graphql-yoast-seo

    Recording of the Livestream

    Recording of the LiveStream Pair Programming with Jason Bahl and Jacob Arriola
  • Pair Programming on WPGraphQL for SEOPress

    SEOPress is a popular SEO Plugin for WordPress.

    Last night I paired with @moon_meister to work on his WPGraphQL for SEOPress plugin. We registered some custom Types and Fields to the WPGraphQL Schema to expose the SEOPress settings to WPGraphQL.

    Screen recording of a pair programming session wit Alex Moon and Jason Bahl, working on the WPGraphQL for SEOPress plugin
  • WPGraphQL and Gatsby Livestream

    Last week I joined Jason Lengstorf on “Learn with Jason” and we built a Gatsby site using WordPress, WPGraphQL and WPGraphQL for Advanced Custom Fields.

    Below is a recording of the livestream.

  • WPGraphQL at WordCamp for Publishers

    I hosted a workshop on “Content Syndication with the WP REST API and WPGraphQL” at the first ever WordCamp for Publishers on Friday, August 18, 2017 at the Denver Post building in beautiful Denver, CO. Unfortunately, there was no video of the workshop, but the slides for the workshop are here: http://slides.com/jasonbahl-1/content-syndication In the workshop, we covered how Digital First Media uses the WP REST API to syndicate millions of pieces of content per year across various WordPress and non-WordPress systems. We cover what we’ve learned about using REST, both positives and negatives. We looked at many of the frustrations we have with working with REST and how GraphQL eases some of those frustrations. We looked at what GraphQL is, how it applies to WordPress as an application data graph, and how you can use GraphQL today in WordPress with the WPGraphQL plugin. We walked through various features of GraphQL using the GraphiQL IDE. We explored the documentation that is generated for the GraphQL Schema using the GraphiQL documentation browser, then we wrote some queries. We queried for posts, posts with nested author objects and nested posts of that author. We looked at node queries, fragments, aliasing, variables and the cursor based pagination, even for data that’s typically not paginated, such as plugins and themes. We then looked at examples of how to extend the WPGraphQL schema to add custom entry points into the queryable WordPress Application Data Graph. The examples we looked at for extending the Schema included:
    • adding a root field
    • adding a custom field to the “post” schema
    • registering a custom post type with WPGraphQL support
    • registering a custom taxonomy with WPGraphQL support
    • adding a custom field to a custom post type where the field can be queried and mutated
    • adding a root field that resolves data from an external system (return a random “dad joke” from icanhazdadjoke.com (source code)
    • Finally, we looked at registering a new Type to the GraphQL schema.
      • We registered a “Syndicated Books” type which resolves with data from external WPGraphQL enabled servers.
    The examples we looked at all exist in a plugin here: https://github.com/wp-graphql/wordcamp-for-publishers-demo-extension WPGraphQL is also going to be one of the projects for Contributor Day at WordCamp for Publishers on Saturday, August 19, 2017 at The Denver Post building. Even if you can’t make it to Denver, feel free to contribute remotely…you can submit issues, start working on pull requests for open issues, test the plugin on your own and write a blog post about it, or just simply star the GitHub repo and tweet about it.
  • WordCamp Orange County Recap

    I had the privilege of leading a Workshop this weekend at WordCamp Orange County on building Single Page Apps with React, Apollo, WordPress and WPGraphQL.

    (more…)

  • Denver WordPress Developer Meetup Workshop Follow Up

    In preparation for the Workshop I’ll be doing at WordCamp Orange County next weekend, I hosted the Workshop for the Denver WordPress Developer Meetup to get some practice.

    I learned a lot about how I need to have my files organized for the workshop and learned what steps I can skip to get through the “important” steps within the allotted time.

    If you attended and have feedback, please share. I’m far from a seasoned professional when it comes to public speaking and hosting workshops, so any feedback is welcome.

    Anyway, below are some links from the Workshop:

    I plan on updating the Tutorial series to have a video walkthrough for each step to compliment the written tutorial, so keep an eye out for that in the next few weeks.

    There’s also a wp-graphql slack organization if you’re interested in discussing the plugin more: https://slackin-wpgraphql.herokuapp.com/ (if that link doesn’t work message me and I’ll invite you to the Slack org).