Blog

  • WPGraphQL v0.8.0

    Release Notes

    TL;DR

    This release focuses on adjusting how Nodes are resolved to prevent errors in cases where the nodes are determined to be considered private and non-viewable by the requesting user. (#1138)

    More details below:

    Schema Breaking Changes

    Schema Breaking changes are changes to the shape of the Schema that would require clients interacting with WPGraphQL to update to remain compatible.

    • n/a: The shape of the Schema remains unchanged in this release. Clients shouldn’t need to adjust their queries/mutations to remain compatible with this release.

    Internal Breaking Changes

    Internal Breaking Changes are changes to internals that might require plugins that extend WPGraphQL to change in order to remain compatible.

    • BREAKING: There are changes to the AbstractConnectionResolver class that affect how connections are resolved. If you are a plugin author extending this class, you will need to update your classes that extend this.
    • BREAKING: Refactored ContentTypeConnectionResolver, TaxonomyConnectionResolver and UserRoleConnectionResolver to not extend the AbstractConnectionResolver class, but instead make use of the Relay::connectionFromArray() method
    • BREAKING: – Update Loaders (CommentLoader, MenuItemLoader, PostObjectLoader, TermObjectLoader, UserLoader) to return an array of resolved nodes (Models) instead of an array of Deferreds.
    • If your plugin extends or calls these classes, you may need to update your code. The loaders now return an array of Nodes (Models) instead of an array of Deferreds.

    New Features

    • Accessibility improvements for the documentation. (See: #1049, #1150) Thanks @jacobarriola!
    • Remove resolveNode config arg from most connections registered by the core plugin as the new method for resolving connections doesn’t wait until the last second to resolve nodes
    • For example: https://github.com/wp-graphql/wp-graphql/compare/master…release/next?expand=1#diff-a04db7b019ce5a16e141cd35799a0718L19-L21, https://github.com/wp-graphql/wp-graphql/compare/master…release/next?expand=1#diff-0b5f575771fc27faf7455a8fa0a05d93L79-L81

    Release Summary

    WPGraphQL makes use of a concept called (Deferred Resolution)[https://github.com/wp-graphql/wp-graphql/pull/722#issue-261315185], to ensure that database queries are executed as efficiently as possible.

    WPGraphQL was deferring the resolution of nodes too late, causing errors when Nodes were determined to be private after being loaded.

    Take the following query for example :

    {
      posts {
         nodes {
             id
             databaseId
             title
         }
      }
    }

    This query might return a payload like so:

    {
      "data": {
        "posts": {
          "nodes": [
            {
              "id": "cG9zdDoyNDI=",
              "databaseId": 242,
              "title": "Test Post"
            },
            {
              "id": "cG9zdDox",
              "databaseId": 1,
              "title": "Hello world!"
            }
          ]
        }
      },
    }

    Looks great! Just what we’d expect (assuming the site had only 2 posts).

    Well, let’s say we had a membership plugin installed (or something similar) that used meta to determine whether a Post should be considered private or not. And let’s say that Post 242 was set to private and should not be returned to a public user.

    Because of how the Deferred resolution was working (before this release), the Post would have been resolved too late to be stripped out of the results, and would return a null within the list of Post nodes, and would include an error like so:

    {
      "errors": [
        {
          "debugMessage": "Cannot return null for non-nullable field Post.id",
          ...
        }
      ],
      "data": {
        "posts": {
          "nodes": [
            null,
            {
              "id": "cG9zdDox",
              "databaseId": 1,
              "title": "Hello world!"
            }
          ]
        }
      },
    }

    This behavior is problematic.

    First, it throws errors, when there really isn’t an error. Nothing has actually gone wrong. The user is asking for Posts, and GraphQL should be able to return posts without error.

    If a Post is private, it shouldn’t be exposed at all. It should be as if it doesn’t even exist in the system. Be returning a null value, we are exposing that something is there behind the scenes.

    The correct behavior should be to return a list of Posts, and no errors returned. If a Post is private, it should simply not be included in the list at all.

    This release fixes this issue by changing how the Deferred resolution of nodes happens.

    Given the query above, resolution used to work like so:

    • Posts: Use WP_Query to get a list of Post IDs. Pass those IDs to the next level of the Resolve Tree
    • Nodes: Use the ID of each node to load the Post using a Batch resolver, pass the Post through the Model Layer to determine if it’s public or private, then either return the Post or return a null value

    Because of the late resolution of the Node, this was causing the Cannot return null for non-nullable field Post.id error. There’s no way to strip a private node out of the list of returned nodes if we’re resolving nodes at the last possible tree in the Graph.

    This pull request changes the behavior to resolve the nodes earlier.

    Given the query above, resolution now works like so:

    • Posts: Use WP_Query to get a list of Post IDs. Pass those IDs to a Deferred Resolver. The Deferred Resolver will make a batch request and load all Nodes, passed through the Model Layer. Nodes that are null will be filtered out now. A list of resolved nodes will be passed to the next level of the Resolve Tree:
    • Nodes: Return the nodes that are passed down. Private nodes will not be passed to this level, so no errors about Cannot return null for non-nullable field Post.id will be returned.

    To accomplish this, some changes to the ConnectionResolver classes were made.

    Now, Nodes are resolved a step earlier, and the resolved nodes are now passed from the Connection down to Edges/Nodes.

    Edges/Nodes now have the full nodes as context in their resolvers, instead of just an ID.

    This can be HUGE when needing to add edge data to connections, where before an entity ID was the only context provided, and that can be too little information to be useful.

    You can read more about a concrete case where the functionality was problematic, and how this release fixes it here: https://github.com/wp-graphql/wp-graphql/issues/1138#issuecomment-580269285

    Changes to the Abstract Connection Resolver

    Below is a list of changes the AbstractConnectionResolver Class. If your Plugin extends this class, the below information should help with upgrading.

    • AbstractConnectionResolver adds the following breaking changes:
    • abstract public function get_loader_name()
      • This method was added to tell the connection resolver what Loader to use to load nodes using Deferred resolution. In order to extend the AbstractConnectionResolver, a Loader will also need to be created. You can see the existing Loaders here.
      • see: https://github.com/wp-graphql/wp-graphql/compare/master…release/next?expand=1#diff-015d5cec0dcc9f802dcfa99bc136f478R252-R260
    • abstract public function get_ids()
      • replaces previous get_items() method
      • This method was added as a way to tell the resolver what IDs we’re dealing with. In many cases, the IDs are returned by the query, and this method can extract them from the Query.
      • see: https://github.com/wp-graphql/wp-graphql/compare/master…release/next?expand=1#diff-015d5cec0dcc9f802dcfa99bc136f478R297
    • abstract public function get_node_by_id()
      • This method was added to tell the loader how to resolve the node as a Model, ensuring it gets properly passed through the Model layer
      • see: https://github.com/wp-graphql/wp-graphql/compare/master…release/next?expand=1#diff-015d5cec0dcc9f802dcfa99bc136f478R328-R335
    • Changed access of some methods from public to protected as they’re not intended to be used outside of the class or extending classes
      • get_amount_requested(), get_offset(), get_query_amount(), has_next_page(), has_previous_page(), get_start_cursor(), get_end_cursor(), get_nodes(), get_cursor_for_node(), get_edges(), get_page_info()

  • Registering GraphQL Fields with Arguments

    One of the most common ways to customize the WPGraphQL Schema is to register new fields.

    When registering fields, argument(s) can also be defined for the field.

    Field arguments in GraphQL allow input to be passed to the field, and when the field is resolved, the input of the field argument can be used to change how the field is resolved.

    Registering Fields

    Below is an example of registering a field with an argument:

    add_action( 'graphql_register_types', function() {
    
      $field_config = [
        'type' => 'String',
        'args' => [
          'myArg' => [
            'type' => 'String',
          ],
        ],
        'resolve' => function( $source, $args, $context, $info ) {
          if ( isset( $args['myArg'] ) ) {
            return 'The value of myArg is: ' . $args['myArg'];
          }
          return 'test';
        },
      ];
    
      register_graphql_field( 'RootQuery', 'myNewField', $field_config);
    });

    Let’s break down the code:

    Hooking into WPGraphQL

    add_action( 'graphql_register_types', function() { ... });

    This action hooks into WPGraphQL when the WPGraphQL Schema is being generated. By hooking our code here, it makes sure our function is only executed when WPGraphQL is being used.

    Define the Field Config

    Within that action, we define a $field_config array which gets passed to the register_graphql_field() function.

    $field_config = [
      'type' => 'String',
      'args' => [
        'myArg' => [
          'type' => 'String',
        ],
      ],
      'resolve' => function( $source, $args, $context, $info ) {
        if ( isset( $args['myArg'] ) ) {
          return 'The value of myArg is: ' . $args['myArg'];
        }
        return 'test';
      },
    ];

    Within the field config we define the following:

    • type: We define the type as “String” to tell WPGraphQL that the field is a String in the Schema
    • args: We define an array of arguments that will be available to the field.
    • resolve: We define a function to execute when the field is queried in GraphQL. Resolve functions in GraphQL always receive 4 arguments ($source, $args, $context, $info). The argument we care about for this example is the 2nd one, $args. We check to see if that argument is set, and if it is, we append the value to the string “The value of myArg is:” and return it. Otherwise we just return the string “test”.

    Register the Field

    And now we can register the field using the $field_config we have defined:

    register_graphql_field( 'RootQuery', 'myNewField', $field_config);

    Here we use the register_graphql_field() function. It accepts 3 arguments:

    • The first argument is the Type in the Schema to add a field to. In this case, we want to add a field to the RootQuery Type.
    • The second argument is the name of the field we are registering. This should be unique on the Type, meaning the Type should not already have a field of this name.
    • The third argument is the $field_config, which we just reviewed.

    The field in action

    We can query this like so:

    query {
      myNewField
    }

    and the results will be:

    {
      "data": {
        "myNewField": "test"
      }
    }

    Now, we can pass a value to the argument like so:

    query {
      myNewField( myArg: "something" )
    }

    and the results will be:

    {
      "data": {
        "myNewField": "The value of myArg is: something"
      }
    }

    Now, you can introduce GraphQL variables like so:

    query MyQuery($myArg:String) {
      myNewField( myArg: $myArg )
    }

    And then you can pass variables to the request. Here’s an example of using a variable in GraphiQL:

    Screen Shot 2020-02-26 at 9 33 03 AM
  • WPGraphQL v0.7.0

    WPGraphQL v0.7.0 is here!
    https://github.com/wp-graphql/wp-graphql/releases/tag/v0.7.0

    This release focused primarily on exposing Post Type and Taxonomy nodes at the root of the query.

    This release does include some breaking changes, so it’s highly recommended that you read the release notes and test on a staging server before updating.

  • 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 v0.6.0 Release

    WPGraphQL v0.6.0 is here!

    This release is one of the most significant releases in the history of the WPGraphQL plugin.

    Breaking Changes

    Before getting into the hype of the release, I want to highlight some prominent breaking changes. A full list can be found in the release notes.

    • A change to pagination of connections was introduced and any plugin/theme that has a class extending the AbstractConnectionResolver will need to add a public method is_valid_offset to support pagination properly. Not adding this method to extending classes will cause errors.
    • Many fields of Post Object Types (Post, Page, Custom) are now only exposed on the Type if that post_type supports the feature. For example, the “page” post type doesn’t support excerpts in core WordPress. Prior to this release you could query the field excerpt when querying for pages. You no longer can ask for the field excerpt on Pages because Pages don’t support excerpts. Posts do support excerpts, so you can query that field on Posts. If you were querying fields that a Post Type didn’t support, this release might break some of your queries.
    • We fixed a bug with settings registered with dashes not properly being mapped to the schema. This fix in behavior could cause a breaking change depending on what types of settings you had registered and were querying from the schema.

    New Features

    This release focused primarily on Interfaces. A full list of new features can be found on the release notes.

    In GraphQL, an Interface is an abstract type that defines a certain set of fields that a type must include to implement the interface.

    In WPGraphQL, prior to this release, the only Interface that was defined and implemented was the node interface, which defined the ID field. Any Type, such as Post, Page, Category, Tag, Comment or User, that implements the node interface must have an ID field. And thus, any of these types could also be fetched by ID.

    Take the following query for example:

    query GET_NODE {
      node(id:"cG9zdDoyMjE5") {
        __typename
        id
        ...on Post {
          title
        }
        ...on Page {
          title
          date
        }
      }
    }

    In the example query, we pass an ID of a node. If, during execution, the server determines the ID is that of a Post, it will return to us the __typename, id, and title, but if it is determined that the ID is that of a Page, it will return the __typename, id, title and date.

    Interfaces allow us to ask for any Type that implements said interface and in response we can ask for the common fields, and can specify the different fields we want based on the Type that is resolved at execution.

    This allows for applications to be built with a high level of predictability.

    Your application is now able to predict what fields will be returned based on the Type being returned. This type of interaction with an API can eliminate entire classes of bugs.

    New Interfaces

    After this release, we now have many more interfaces to join the Node interface.

    The following are Interfaces introduced in this release:

    • TermNode: Defines fields shared across Terms of any Taxonomy Type
    • ContentNode: Defines common fields shared by Post object nodes. Not all fields of Post Types are common across all Post Types. Each Post Type can register/deregister support for numerous fields. The fields that cannot be different are defined in this interface and all Post types implement this Interface.
    • UniformResourceIdentifiable: Defines a uri field. Implemented by Nodes that can be accessed by URI.
    • NodeWithTitle: Defines a title field. Implemented by post types that support titles.
    • NodeWithContentEditor: Defines the content field. Implemented by post types that support the editor.
    • NodeWithAuthor: Defines the author field, connecting a Node to its author. Implemented by post types that support authors.
    • NodeWithFeaturedImage: Defines the featured image field, connecting a Node to its featured image. Implemented by post types that support thumbnails.
    • NodeWithComments: Defines comment fields. Implemented by post types that support comments.
    • HierarchicalContentNode: Defines hierarchical fields (parent/children). Implemented by hierarchical post types.
    • ContentTemplate: Defines a template field.
    • NodeWithRevisions: Defines revisions fields. Implemented by post types that support revisions.
    • NodeWithExcerpt: Defines the excerpt field. Implemented by post types that support excerpts.
    • NodeWithTrackbacks: Defines fields related to trackbacks and pingbacks. Implemented by post types that support trackbacks.

    I cannot provide examples of using every one of these interfaces, but I will cover some examples that should be valuable to WPGraphQL users.

    Content Node Interface

    The ContentNode interface allows for nodes of different Post Types to be queried in a single connection. Prior to this release, Posts of each type were fetched from their dedicated entry points. For example, you could query for { posts { ...fields } } or { pages { ...fields } }, but now, you can query a single collection spanning many post types.

    Here’s an example:

    {
      contentNodes {
        nodes {
          __typename
          id
          link
          uri
          ... on Page {
            isFrontPage
          }
        }
      }
    }

    And we might see results like the following:

    {
      "data": {
        "contentNodes": {
          "nodes": [
            {
              "__typename": "Post",
              "id": "cG9zdDoyMjE5",
              "link": "http://acf.local/2020/01/14/test-revisions/",
              "uri": "2020/01/14/test-revisions/"
            },
            {
              "__typename": "Page",
              "id": "cGFnZToxNjQw",
              "link": "http://acf.local/test-page-revision/",
              "uri": "test-page-revision/",
              "isFrontPage": false
            },
          ]
        }
      }
    }

    In this example, we can see that we are receiving both a Post and a Page in the same connection query, and are able to specify common fields, but also able to specify a specific field we want if the Type is Page.

    This flexibility allows WPGraphQL users to create robust User Interfaces with flexibility, but also predictability.

    URI Interface

    The UniformResourceIdentifiable Interface added in this release allows for any node that can be identified by URI to be queried for from a single entry point.

    This is incredibly powerful for headless WordPress applications.

    When interacting with a WordPress site, it’s incredibly common for a user to have a URI, a path to a resource. Typically that path is entered into a browser, and WordPress is able to return the proper page for that resource. The template and data displayed by WordPress is different based on the type of resource (page, post, user, term) it is.

    WPGraphQL can now interact the same way.

    A user can pass a URI to WPGraphQL, and specify what it wants in return based on the Type that the resource resolves as.

    The following example shows how to query for a nodeByUri for a Post, Page, Tag, Category and User, specifying different fields for each Type.

    {
      page: nodeByUri(uri: "about/") {
        ...URI
      }
      post: nodeByUri(uri: "2019/12/05/test-5/") {
        ...URI
      }
      tag: nodeByUri(uri: "tag/8bit/") {
        ...URI
      }
      category: nodeByUri(uri: "category/alignment/") {
        ...URI
      }
      user: nodeByUri(uri: "author/jasonbahl/") {
        ...URI
      }
    }
    
    fragment URI on UniformResourceIdentifiable {
      __typename
      uri
      ... on Page {
        pageId
      }
      ... on Post {
        postId
      }
      ... on Category {
        categoryId
      }
      ... on Tag {
        tagId
      }
      ... on User {
        userId
      }
    }

    And the response might look like:

    {
      "data": {
        "page": {
          "__typename": "Page",
          "uri": "about/",
          "pageId": 1086
        },
        "post": {
          "__typename": "Post",
          "uri": "2019/12/05/test-5/",
          "postId": 1739
        },
        "tag": {
          "__typename": "Tag",
          "uri": "tag/8bit/",
          "tagId": 45
        },
        "category": {
          "__typename": "Category",
          "uri": "category/alignment/",
          "categoryId": 4
        },
        "user": {
          "__typename": "User",
          "uri": "author/jasonbahl/",
          "userId": 1
        }
      },
    }

    This is incredibly powerful for headless applications that might only have a URI of a resource to identify the resource by. Now headless applications can resolve resources with a high level of predictability, even without knowing the type of resource up front.

    What’s next

    Now that this monumental release is here, the next release will have a focus on Connections. There are many bugs and new features that we want to address that affect the behavior of connections. Keep an eye out for more details there.

  • WPGraphQL v0.4.1

    Today we released WPGraphQL v0.4.1. This is a non-breaking change release. You can view the release here: https://github.com/wp-graphql/wp-graphql/releases/tag/v0.4.1

    Release Summary

    • Update composer dependencies
    • Update filter on WPGraphQL::get_allowed_taxonomies()
    • Update to Docs for WPGraphQL for ACF – Thanks @henrikwirth!
    • Update to Install and Activate Docs – Thanks @jacobarriola!
    • Update to Docs sidebar styles – Thanks @TylerBarnes!
    • Add Router::is_graphql_request() method – Thanks @esamattis!
    • Fix issue with PostObjectMutation not respecting term inputs if graphql_plural_name was capitalized in the registration
    • Prevent execution of connections if the source Post is null
    • Fix PostObjectCursor issue with meta_value_num compares – Thanks @kidunot89!
    • Fix UserLoader for orphaned users – Kellen
    • Adjustments to Comment Model – Ryan
    • Add Interface inheritance for Types implementing Interfaces – Thanks @kidunot89!
    • Fix PHP warning in InstrumentSchema.php
    • Add tests for Interfaces
    • add Page.isFrontPage field to the Schema – Thanks @zgordon!

  • WPGraphQL at the Desert GraphQL Meetup

    Last night I had the pleasure of remotely presenting for the Desert GraphQL Meetup in Phoenix, Arizona.

    TL;DR, Here’s the video

    Video recording of the GraphQL for WordPress meetup presentation

    Here’s the slides:

    https://slides.com/jasonbahl-1/desert-graphql-oct-2019#/

    Summary of the presentation

    In the presentation I covered the history of WPGraphQL, why the plugin exists, and why it makes sense to continue building for the wider WordPress community.

    We look at some of the frustrations with the WordPress REST API that lead to the creation of WPGraphQL.

    I quickly reviewed what WordPress looks like, as in what the WordPress Dashboard looks like and what it enables out of the box.

    Next, I show how activating WPGraphQL activates a GraphQL API for your WordPress and the WPGraphiQL Plugin adds the GraphiQL IDE to your WordPress dashboard.

    We then look at what WordPress looks like, visually, as an Application Data Graph and how to use GraphQL Queries to pick trees out of that graph.

    Next we looked at various live examples of GraphQL Queries and explored features of the GraphQL Query Language, such as querying multiple resources in a single request, using variables, arguments, fragments, aliases and directives.

    We then compare how to build the same slice of UI using PHP, REST and GraphQL to show how much lower effort it is to use GraphQL compared to alternatives.

    I finish up talking about some things I’ve learned while building WPGraphQL, and what’s next now that I am part of the GatsbyJS team.

  • Registering Custom Connections with Daniel Olson

    Yesterday I had the pleasure of pair-programming with Daniel Olson of Shifter and we walked through the process of registering a custom connection in the WPGraphQL Schema, and writing the resolvers for it.

    You can watch the recording of the pair programming session below:

  • Release v0.3.5

    Today we released v0.3.5 of WPGraphQL.

    Full release notes here: https://github.com/wp-graphql/wp-graphql/releases/tag/v0.3.5

  • WPGraphiQL IDE v1.0.0 with GraphQL Explorer

    The GraphiQL IDE is an incredibly helpful tool when using GraphQL. WPGraphiQL has been the go-to GraphiQL IDE for use with WPGraphQL, and today we’ve released v1.0.0 of the WPGraphiQL IDE, now with the AMAZING GraphQL Explorer feature, courtesy of the fine folks at OneGraph.com.