Blog

  • WPGraphQL for FacetWP

    I am pleased to announce the beta release of my FacetWP integration plugin for WPGraphQL. 

    The main functionality was written quickly in late 2018 to fill a need to provide client-side access to FacetWP’s index and filtering functionality via WPGraphQL. As a proof-of-concept it worked exactly as needed. It was a lot less effort to create this plugin than it would have been to roll my own facet indexing module. 

    Recently, I have had some down time between changing jobs and have been able to refactor and clean up the original code into something more usable by the larger WordPress and WPGraphQL community. If you have the need to expose the functionality of FacetWP via the GraphQL schema, I hope you find this plugin helpful!

    Usage

    To register WP Post Type(s) to be added to the schema, simply use the facet query registration function register_graphql_facet_type() as follows: 

    // Register facet for Posts 
    register_graphql_facet_type( 'post' ); // replace post with your CPT name as necessary 

    Prerequisites: To use the plugin users must have already configured at least one facet in FacetWP and configured WPGraphQL.

    Input

    The plugin uses reflection to expose all configured facets are available for querying. Each facet is added as an input argument to the query. 

    Different facet types have unique inputs (eg a Slider has a min and max while Checkboxes is a simple array of strings). 

    Given the post registration above the query variables might look something like this: 

    const variables = { 
        after: '', // Use endCursor for paging 
        query: { 
            // example facet filtered on post ID 
            facet_name: [1, 2, 3], 
            // sample facet filtered on taxonomy term 
            term_facet_name: ['category-1', 'category-3'], 
        }, 
        orderBy: { 
            field: 'DATE', 
            order: 'DESC', 
        }, 
    } 

    Payload

    Registering a facet type will result in a new field on the Root Query. The field name is determined by the type to be queried. 

    The payload of the new facet field includes: 

    • facets: The returned facet information
    • type connection: A WPGraphQL connection to the post type query 

    Given the post registration earlier, the query and response payload might look like this: 

    postFacet(where: {status: PUBLISH, query: $query}) { 
      facets { 
        selected 
        name 
        label 
        choices { 
          value 
          label 
          count 
        } 
      } 
      posts(first: 10, after: $after, where: {search: $search, orderby: $orderBy}) { 
        pageInfo { 
          hasNextPage 
          endCursor 
        } 
        nodes { 
          title 
          excerpt 
        } 
      } 
    } 

    Facets

    This payload includes all information relating to the queried facets and input variables. Available choices (including available count), settings and other relevant information are exposed in this field. 

    Connection

    This payload is a connection to the configured post type. It accepts all the pertinent GraphQL arguments (paging, sorting etc). 

    This connection only returns posts that match the facet filter. 

    Conclusion 

    This plugin provides users of WPGraphQL with access to an established and popular WordPress plugin for server-side filtering; FacetWP. I hope the WordPress community finds it helpful. It certainly was a great time saver for our project. 

    Disclaimer and limitations: I have thoroughly tested Checkbox, fSelect and Radio facet types, as these are used in my app. There is provisional support for all other facet types, however these are not fully tested. As with any open source project, a detailed issue (or better yet a pull request) would be appreciated. 

    Please see GitHub for further documentation and commentary.

  • WPGraphQL featured on Syntax.fm

    Today, I had the pleasure of being featured on the Syntax.fm podcast with hosts Wes Bos and Scott Tolinsky.

    In this episode we discussed how to use GraphQL with WordPress using the WPGraphQL plugin. I also announced that I’ve joined the Gatsby team to work on WPGraphQL an its immediate ecosystem full-tiime.

    Below is the recording of the podcast.

  • WPGraphQL for ACF is here!

    Many folks using WPGraphQL have asked about how to use GraphQL with their ACF data. The answer has typically been to manually map their ACF fields to the GraphQL schema using functions like register_graphql_field and register_graphql_object_type. For a few fields, this isn’t much of an issue, but when you have dozens of fields, this gets very tedious.

    Today, we’ve released WPGraphQL for Advanced Custom Fields, which automatically adds your Advanced Custom Fields to your WPGraphQL Schema.

    You can learn more about the plugin here: https://www.wpgraphql.com/acf

    Very excited to see what the community builds with this. If you have any questions, please contact us.

  • Upgrading to v0.3.0

    WPGraphQL v0.3.0 is one of the most substantial releases to date for the plugin. You can read more about what was included in this release here and here.

    Test in Staging Environment

    It’s best practice to test plugin updates in staging environment, but I want to re-iterate that again here. Make sure you thoroughly test things when you update this plugin to v0.3.0, especially if you have any custom code that extends the Schema in any way.

    Break: “roles” field on User type has changed shape

    The User Type in the Schema previously had a roles field that returned a list of role names as strings.

    Pre v0.3.0

    {
      users {
        nodes {
          roles
        }
      }
    }

    Would return something like the following:

    {
      "data": {
        "users": {
          "nodes": [
            {
              "roles": [ 'administrator' ]
            }
          ]
        }
      }
    }

    v0.3.0

    In v0.3.0, the roles field has changed to a Connection of roles. The query would change to something like the following:

    {
      users {
        nodes {
          roles {
            nodes {
              name
            }
          }
        }
      }
    }

    This change was made largely because Roles aren’t _really_ properties of a user. Roles are defined as entities on their own, and Users have a connection to one or more roles.

    Break: Resolvers return models instead of WP_* instances

    If you were extending some of the core Types in the Schema by adding fields, the first argument passed to the resolver used to be an instance of a core WP_* class. For instance, the Post Type would pass an instance of WP_Post down to field resolvers on that Type. Now, it will pass an instance of \WPGraphQL\Model\Post.

    Here’s an example of a Field that may have been registered to the “old” version of the Schema:

    register_graphql_field( 'Post', 'myNewField', [
      'type' => 'String',
      'resolve' => function( \WP_Post $post, $args, $context, $info ) {
        $data = get_post_meta( $post->ID, 'some_data', true );
        return $data ? data : null;  
      }
    ] );

    Here, we see the first argument passed down is a \WP_Post. In v0.3.0, that would be a \WPGraphQL\Model\Post like so:

    register_graphql_field( 'Post', 'myNewField', [
      'type' => 'String',
      'resolve' => function( \WPGraphQL\Model\Post $post, $args, $context, $info ) {
        $data = get_post_meta( $post->ID, 'some_data', true );
        return $data ? data : null;   
      }
    ] );

    The following resolvers changed:

    • \WP_Post is now \WPGraphQL\Model\Post
    • \WP_User is now \WPGraphQL\Model\User
    • \WP_Comment is now \WPGraphQL\Model\Comment
    • \WP_Term is now \WPGraphQL\Model\Term

    The full list of Models now available, that replaced what was there previously, is:

    • \WPGraphQL\Model\Avatar
    • \WPGraphQL\Model\Comment
    • \WPGraphQL\Model\CommentAuthor
    • \WPGraphQL\Model\Menu
    • \WPGraphQL\Model\MenuItem
    • \WPGraphQL\Model\Plugin
    • \WPGraphQL\Model\Post
    • \WPGraphQL\Model\PostType
    • \WPGraphQL\Model\Taxonomy
    • \WPGraphQL\Model\Term
    • \WPGraphQL\Model\Theme
    • \WPGraphQL\Model\User
    • \WPGraphQL\Model\UserRole

    Depending on how you were extending the Schema, this may or may not have any affect on you.

    Break: DataSource methods

    The signature of DataSource methods have changed. If you were calling any DataSource methods in your extensions, you’ll likely need to refactor a bit. We recommend checking out the changes to that file so you understand all the changes for the methods you’re using, but here’s a quick example pointing out the difference:

    Pre v0.3.0:

    DataSource::resolve_post_object( $id, $post_type );

    v0.3.0

    DataSource::resolve_post_object( $id, $context );

    The specific way in which the signature for the DataSource method changed may be different depending on the method. If you’re calling any of these methods, check how it changed to be sure you’re using it the new way.

    Break: Connection Resolver Classes

    If you were making use of the ConnectionResolver classes in anyway, they’ve been completely revamped. They’ve been moved into the /Data/Connection directory, and are now stateful classes instead of classes with static methods. They are now much easier to digest and understand how to extend and create your own Connection Resolvers.

    I won’t cover changes in detail here, but if you wrote any custom Connection Resolvers, and the new way of doing it doesn’t make sense after reading the new code, feel free to reach out and we can do our best to help you update.

    Break: All kinds of restricted content is actually restricted now

    With the introduction of the Model Layer, many fields and nodes that were previously accessible in public requests have now been restricted.

    For example, our documentation previously mentioned that fields such as email on the User type was publicly exposed, and that if you wanted to hide it, you could do so via filter.

    We’ve implemented a formal Model Layer that now handles restrictions like this out of the box, so fields that you may have been querying before, might not be returned anymore. Depending on your use case, this may be a break to you application, so it’s something to get familiar with.

    How the Model Layer Works

    We’ve written some docs on the Model Layer, but the general idea is that if you download install WordPress with nothing active but a standard `twenty-*` theme, the data that is exposed to a user should be the same data that is exposed to a GraphQL request.

    For example, a non-authenticated user cannot access draft Posts in a vanilla WP install, so WPGraphQL will not return Draft posts to non authenticated users. (There’s more logic than that, but that’s just an example).

    There are dozens of conditions in place that help determine whether fields should or should not be returned to the consumer making the request.

    Other Breaking Changes

    There might be some other breaking changes that we didn’t cover here. If you read this guide and also get familiar with the latest info on https://docs.wpgraphql.com, but still run into issues that break your application after updating to v0.3.0, please reach out in Slack or Spectrum, or Open a Github Issue and we’ll do our best to help you get upgraded. We also highly recommend getting familiar with the source code of v0.3.0. We’ve commented the code very heavily to help provide an understanding of how everything is working and why it’s working that way.

    If you find something you believe is a bug, as usual, please open an issue on Github

  • What’s coming in v0.3.0?

    The next release of WPGraphQL will be version v0.3.0, and is slated to be released next week.

    This release is arguably the most substantial release to date. It’s going to made up of 2 major features: “DataLoader” and “Model Layer

    DataLoader

    The role of DataLoader is to load data as efficiently as possible. You can read some of the highlights in the PR comment here: https://github.com/wp-graphql/wp-graphql/pull/722#issue-261315185

    Model Layer

    The goal of the Model Layer is to centralize access control to objects and properties throughout the Graph. In short, that means we just want to make things more secure.

    At the moment, some potentially sensitive data is exposed by default, and it’s the site owner’s responsibility to filter and adjust the Schema and resolvers to only expose data they want to be exposed in the API. You can read more about that here (under the Sensitive Data heading): https://docs.wpgraphql.com/getting-started/users

    The Model Layer switches the plugins stance to be more restrictive by default, and allow site owners to loosen the restrictions when necessary, instead of expecting site owners to go through the effort of limiting potentially private content out of the box. Of course, each application is different, and our defaults may not be what you need for your application, so you will be able to filter access at a very granular level to make the GraphQL API work best for your specific needs.

    The Model Layer centralizes access checks by taking into consideration the “current_user” making the request, then determines what objects (Posts, Terms, Comments, etc) and fields of those objects (Title, Content, etc) should be exposed to the user making the request. Fields like user emails will now only be exposed by default to authenticated users with “list_users” capabilities, for example. If you want to make override that new default and expose a field that we have restricted, you’ll have the ability to control that via various filters.

    We’ll be working on a formal upgrade guide, as there will be numerous breaking changes for this release, but we think they’re all very welcome breaking changes.

    You can keep an eye on progress in the branches here:

    Security Audit

    Over the past weekend we received the results of a Security Audit performed by Simone Quatrini of Pen Test Partners. The security audit pointed out some Insecure Access Control violations. Most of the violations in the report had already been resolved by the in-progress Model Layer branch, such as exposed user email addresses, which, as mentioned above is documented here.

    There were 2 issues presented that we were not on our radar and we are grateful to Simone for bringing to our attention. We will be patching shortly as part of the v0.3.0 release. One issue, in particular, will be fixed for previous versions of the plugin as well, and details will be published about that once it has been resolved.

    Full details of the Security Audit and resolutions to the issues will be published after the v0.3.0 release and other mentioned patches have been released.

  • WPGraphQL at WordCamp Montreal


    Étienne Bélanger gave a talk at WordCamp Montreal 2018 titled: “Your First Headless WordPress Project with ReactJS and GraphQL”.

    In the talk, he covers some basic differences between the WP REST API and WPGraphQL, then shows how to use React and Apollo Client to consume data from a WordPress site with the WPGraphQL plugin active.

    Great talk, Étienne! – and thanks for sharing the video with us.

  • WPGraphQL + Gatsby Tutorial

    Zac Gordon put together a group of engineers to work on a formal project, GatsbyWPThemes.com to port popular WordPress themes to Gatsby themes.

    Recently, Muhammad Muhsin published a tutorial showcasing how users can create a Gatsby theme using WordPress as the CMS and WPGraphQL as the API for Gatsby to consume data from.

    Take a look at the tutorial, and let us know what you build with Gatsby and WPGraphQL!

  • Release v0.2.3

    Today we released a new version of WPGraphQL, v0.2.3.

    The release can be found here: https://github.com/wp-graphql/wp-graphql/releases/tag/v0.2.3

    Below is a recap of the release notes:

    Breaking Changes

    • none

    New Features

    • #676: New TimezoneEnum type added to the Schema
    • #719: New graphql_menu_item_connection_args and graphql_menu_item_connection_query_argsfilters added to the MenuItemConnectionResolver. Thanks @epeli!

    Bug Fixes

    • #714 Fixes global $post not properly being setup before resolving fields for posts. Thanks @epeli!
    • #716: Adjusts how the MenuLocationEnum values are determined to include all locations, not just locations with a menu already assigned.

  • Easy static HTML exports of your Next.js + GraphQL site

    You’re here because you’d like to learn how to create static HTML exports for your Next.js site which uses GraphQL as a data source to create dynamic pages from page components. And that site may even use WPGraphQL to pull content from WordPress.

    Fantastic, this article will describe the simple process of doing exactly that.

    Generally speaking, all else being equal, serving a static HTML file as a webpage is generally going to be the fastest way to get that page in your users hands. There are exceptions to every rule, but this is a pretty safe bet. This speed is great for SEO, UX, conversion rates, blah blah blah, but building a site that delivers that fast is also just fun!

    Bare with me for a few moments as we setup the solution with a bit of boilerplate.

    As you will see if you peruse the interactive Next.js docs, they have a simple method of enabling static HTML exports. Create a file in your project at the root directory called next.config.js, and add the following code:

    module.exports = {
      exportPathMap: function () {
        return {
          '/': { page: '/' }
        }
      }
    }

    Then add the following to package.json:

    {
      "scripts": {
        "build": "next build",
        "export": "next export"
      }
    }

    And then run:

    npm run build
    npm run export

    Now checkout the out directory. You’ll see static HTML file for your index route. You can now run the following to see your site in action:

    npm install -g serve
    cd out
    serve -p 8080

    The Next.js docs then extend this example with the following code snippet:

    module.exports = {
      exportPathMap: function () {
        return {
          '/': { page: '/' },
          '/about': { page: '/about' },
          '/p/hello-nextjs': {page: '/post', query: { title: 'Hello Next.js' } },
          '/p/learn-nextjs': { page: '/post', query: { title: 'Learn Next.js is awesome' } },
          '/p/deploy-nextjs': { page: '/post', query: { title: 'Deploy apps with Zeit' } },
          '/p/exporting-pages': { page: '/post', query: { title: 'Learn to Export HTML Pages' } }
        }
      }
    }

    What we are doing here on the lines starting with '/p…' is passing query data to the component at '/post'.

    Ok, now the problem becomes clear: we need a data source to populate the query (or params, title, date, etc.) that our components rely on. But the next export doesn’t “hook into” the GraphQL data source you have likely already setup in your _app.js.

    So does this mean you need to rewire everything to hook up GraphQL, write fetching logic, looping, etc?

    No, it means I had to. You can just install the NPM package I created and do some basic configuration. 🙂

    Enter next-graphql-static-export:

    A minimal package which provides the functionality required to perform a static HTML export of dynamic pages from Next.js when using GraphQL as a data source.
    https://www.npmjs.com/package/next-graphql-static-export

    Ok, end of boilerplate setup. Let’s get down creating your Next.js+GraphQL+WPGraphQL+static-HTML-exported site.

    By the way, you do not need to include WPGraphQL in your project to make use of this method. Though if your site has a WordPress backend, you probably should.

    There are no assumptions in the code for the NPM package which rely on WPGraphQL, other than possibly the schema.

    Here is the relevant bit of the WPGraphQL schema in a query result:

    {
      "data": {
        "posts": {
          "pageInfo": {
            "endCursor": "PQOXYXljb25uZWN0oT6uOjEwMzg5Mg==",
            "hasNextPage": true
          },
          "nodes": [
            {
              "id": "cG3zdDozMDO3MTA=",
              "uri": "why-you-should-use-wpgraphql",
              "title": "Why You Should Use WPGraphQL"
            }
          ]
        }
      }
    }

    Notice the nesting of pageInfo, endCursor and hasNext page, along with nodes. It’s worth taking a quick look at the results you get from queries on your project if you are not using WPGraphQL. If your GraphQL server does not return data in this format, no problem, you can still easily use this package. You’ll just need to write a custom query result parsing function. Details below.

    We’ll assume for the moment that your GraphQL server does have the same schema as WPGraphQL and move on.

    First install ‘next-graphql-static-export’:

    npm i next-graphql-static-export

    Now create a new config-exports.js file in the root directory of your project, where we will configure the parameters that we pass to the processContent made available via the next-graphql-static-export library. The following is an example of sensible defaults:

    // Include our queries. See https://docs.wpgraphql.com/getting-started/posts for basic queries
    const postsQuery = require("./queries/posts-query");
    const pagesQuery = require("./queries/pages-query");
    const productsQuery = require("./queries/products-query");
    
    // Your graphql endpoint
    const endpoint = `https://www.website.com/graphql`;
    
    // The custom information we pass to properly fetch data for each 'post type' or content type, the result of which is the object that Next.js needs to produce an accurate static HTML export of our site
    const typeParams = [
      {
        pageComponent: "page",
        contentType: "pages",
        query: pagesQuery,
        urlBase: "pages",
        perPage: 100,
        endpoint
      },
      {
        pageComponent: "product",
        contentType: "products",
        query: productsQuery,
        urlBase: "products",
        perPage: 100,
        endpoint
      },
      {
        pageComponent: "article",
        contentType: "posts",
        query: postsQuery,
        urlBase: "articles",
        perPage: 100,
        endpoint
      }
    ];
    
    module.exports = {
      typeParams
    };

    Open up your package.json again, and add change the line pertaining to the dev script to the following:

    "scripts": {
        "dev": "EXPORT=false node server.js",
        ...
    }

    This EXPORT env var will allow us to continue to use npm run dev to develop our site, yet avoid doing a full static HTML export every time we change a line of code (see next step for where we use this env var).

    Open up your next.config.js again, and make it look similar to the following:

    const processContent = require("next-graphql-static-export");
    const { typeParams } = require("./config-export");
    
    module.exports = {
      exportPathMap: async () => {
        if (EXPORT === "false") return {}; // This is where we use the env var we just added to the dev script.
    
        const [pages, products] = await processContent(typeParams);
    
        // Create the static pages with Next
        return {
          "/": { page: "/" }
          // ...pages,
          // ...products
          // ...posts
        };
      }
    };

    Now go ahead and run the following again:

    npm run build
    npm run export
    cd out
    serve -p 8080

    Open up localhost:8080 and you should see your beautiful site with all dynamic page fully rendered and served as static HTML!

    Passing your own query result parsing function

    If your schema differs from the one shown above, then you will need to pass your own query result parsing function to your typeParams in your config-export.js file. Here is an example function which exactly reproduces the existing functionality:

    const parseQueryResults = (queryResponse, contentType) => {
      const {
        [contentType]: {
          nodes,
          pageInfo: { hasNextPage, endCursor } 
        }
      } = queryResponse;
      return { nodes, hasNextPage, endCursor };
    };

    Note that the function takes in the queryResponse, and the current contentType, both of which are used to destructure the query result and return the following required values: nodes, hasNextPage, endCursor.

    You can use any amount of logic in this function, but those values are required.

    To use this function in your implementation, simply add the function as a parameter to the relevant content types. So our new config-exports.js would look like this:

    // Include our queries. See https://docs.wpgraphql.com/getting-started/posts for basic queries
    const postsQuery = require("./queries/posts-query");
    const pagesQuery = require("./queries/pages-query");
    const productsQuery = require("./queries/products-query");
    
    // Your graphql endpoint
    const endpoint = `https://www.website.com/graphql`;
    
    // Custom query result parsing function
    const parseQueryResults = (queryResponse, contentType) => {
      const {
        [contentType]: {
          nodes,
          pageInfo: { hasNextPage, endCursor } 
        }
      } = queryResponse;
      return { nodes, hasNextPage, endCursor };
    };
    
    // The custom information we pass to properly fetch data for each 'post type' or content type, the result of which is the object that Next.js needs to produce an accurate static HTML export of our site
    const typeParams = [
      {
        pageComponent: "page",
        contentType: "pages",
        query: pagesQuery,
        urlBase: "pages",
        perPage: 100,
        endpoint,
        parseQueryResults // Passing our function as a parameter
      },
      {
        pageComponent: "product",
        contentType: "products",
        query: productsQuery,
        urlBase: "products",
        perPage: 100,
        endpoint,
        parseQueryResults
      },
      {
        pageComponent: "article",
        contentType: "posts",
        query: postsQuery,
        urlBase: "articles",
        perPage: 100,
        endpoint,
        parseQueryResults
      }
    ];
    
    module.exports = {
      typeParams
    };

    And that’s all there is to adapting this module to a different schema than the one natively expected. Feel free to reach out with any questions.

  • Preventing unauthenticated requests to your WPGraphQL API

    Someone asked in the Slack channel how they could lock down the WPGraphQL endpoint so that only authenticated users could access it.

    Provided Solution

    add_action( 'do_graphql_request', 'force_graphql_api_authentication', 10, 1 );
    
    function force_graphql_api_authentication( $query ) {
    	if ( ! defined( 'GRAPHQL_HTTP_REQUEST' ) || true !== GRAPHQL_HTTP_REQUEST ) {
    		return;
    	}
    
    	$introspection_query = \GraphQL\Type\Introspection::getIntrospectionQuery();
    	$is_introspection_query = trim($query) === trim($introspection_query);
    
    	if ( $is_introspection_query ) {
    		return;
    	}
    
    	if ( ! get_current_user_id() ) {
    		throw new \GraphQL\Error\UserError( __( 'You do not have permission to access the API', 'your-textdomain' ) );
    	}
    }

    What this does

    Below, I’ll walk through what this snippet does.

    Hook into the GraphQL request lifecycle

    add_action( 'do_graphql_request', 'force_graphql_api_authentication', 10, 1 );

    This snippet hooks into the do_graphql_request action, which is fired when a GraphQL request is about to be executed, and fires the function force_graphql_api_authentication

    The action passes 4 args to the force_graphql_api_authentication callback: $query, $operation, $variables and $params. For this particular case, we only care about the first argument, $query, which is the query string to be executed.

    Determine if the request is an HTTP Request

    Since WPGraphQL can be used internally within your Plugin and Theme PHP code to hydrate data for page templates, shortcodes, etc, locking down all GraphQL requests could have unintentional consequences, so we don’t want to prevent all unauthenticated requests from executing with GraphQL, we just want to prevent unauthenticated requests coming over HTTP.

    So we first check:

    if ( ! defined( 'GRAPHQL_HTTP_REQUEST' ) || true !== GRAPHQL_HTTP_REQUEST ) {
      return;
    }

    This checks to see if the request is indeed a GraphQL HTTP Request. If it’s not, it simply returns and we let GraphQL carry on as usual. That means internal GraphQL requests using the graphql() function can be processed as usual.

    Ignore Introspection Queries

    GraphQL has an awesome feature where the Schema itself is queryable. Tools such as WPGraphiQL, GraphQL Playground and Altair make use of the IntrospectionQuery to fetch the Schema and render Schema documentation for users.

    $introspection_query = \GraphQL\Type\Introspection::getIntrospectionQuery();
    $is_introspection_query = trim($query) === trim(introspection_query);
    
    if ( $is_introspection_query ) {
      return;
    }

    Here we use a helper method from the underlying GraphQL PHP library which is part of WPGraphQL to get the Introspection Query.

    $introspection_query = \GraphQL\Type\Introspection::getIntrospectionQuery();

    Then, we compare the incoming query, which is passed through the do_graphql_request action to check if the incoming query is an IntrospectionQuery or not:

    $is_introspection_query = trim($query) === trim(introspection_query);

    And last, if we’ve determined that the incoming query is indeed an IntrospectionQuery, we return and allow GraphQL to execute as normal. This will allow GraphQL to execute the Introspection Query and send the Schema back to the tool requesting it.

    if ( $is_introspection_query ) {
      return;
    }

    Throw an error if the request is not authenticated

    Lastly, we check to see if the request is authenticated by checking for the ID of the current user. If the ID is “0”, then the request is not authenticated, so we want to throw an error.

    if ( ! get_current_user_id() ) {
    	throw new \GraphQL\Error\UserError( __( 'You do not have permission to access the API', 'your-textdomain' ) );
    }

    Conclusion

    With this snippet, you can lock down your WPGraphQL endpoint so nothing will be executed if the request is not authenticated.

    If you need to make authenticated requests, we recommend using WPGraphQL JWT Authentication, but any of the Auth plugins that work for the REST API plugin _should_ work well with WPGraphQL as well.


    NOTE:

    The Application Passwords plugin requires a filter to play nice with WPGraphQL:

    add_filter( 'application_password_is_api_request', function( $api_request ) {
      if ( defined( 'GRAPHQL_HTTP_REQUEST' ) && true === GRAPHQL_HTTP_REQUEST ) {
         return true;
      }
      return $api_request;
    });