WP Manifestindependent plugin directory
manifest / integrations / wp-graphql-facetwp

WPGraphQL for FacetWP WP Registry grade B. Medium-severity findings · 1 findingWP RegistryMedium-severity findings · 1 findingOpen the reportB

WPGraphQL FacetWP integration plguin

★ 35stars
124release downloads
≈10active sites
100composer installs
0.5.2latest · 1 year ago

Install

Download wp-graphql-facetwp.zip From the 0.5.2 release on GitHub, Jun 7, 2025

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/axewp/wp-graphql-facetwp/releases/download/0.5.2/wp-graphql-facetwp.zip

On Packagist as hsimah-services/wp-graphql-facetwp:

composer require hsimah-services/wp-graphql-facetwp
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Logo

WPGraphQL for FacetWP

Adds WPGraphQL support for FacetWP.

Packagist License Packagist Version GitHub commits since latest release (by SemVer) GitHub forks GitHub Repo stars


Overview

This plugin exposes configured facets through the graph schema. Once registered for a type, a query is available. The payload includes both facet choices and information and a connection to the post type data. This allows for standard GraphQL pagination of the returned data set.

This plugin has been tested and is functional with SearchWP.

System Requirements

  • PHP 7.4-8.1.x
  • WordPress 5.4.1+
  • WPGraphQL 1.6.0+ (1.9.0+ recommended)
  • FacetWP 4.0+

Quick Install

  1. Install & activate WPGraphQL.
  2. Install & activate FacetWP.
  3. Download the wp-graphql-facetwp.zip file from the latest release upload it to your WordPress install, and activate the plugin.

[!IMPORTANT]

Make sure you are downloading the wp-graphql-facetwp.zip file from the releases page, not the Source code (zip) file nor a clone of the repository.

If you wish to use the source code, you will need to run composer install inside the plugin folder to install the required dependencies.

With Composer

composer require hsimah-services/wp-graphql-facetwp

Updating and Versioning

As we work towards a 1.0 Release, we will need to introduce numerous breaking changes. We will do our best to group multiple breaking changes together in a single release, to make it easier on developers to keep their projects up-to-date.

Until we hit v1.0, we're using a modified version of SemVer, where:

  • v0.x: "Major" releases. These releases introduce new features, and may contain breaking changes to either the PHP API or the GraphQL schema
  • v0.x.y: "Minor" releases. These releases introduce new features and enhancements and address bugs. They do not contain breaking changes.
  • v0.x.y.z: "Patch" releases. These releases are reserved for addressing issue with the previous release only.

Development and Support

WPGraphQL for FacetWP was initially created by Hamish Blake. Maintenance and development are now provided by AxePress Development. On 15 February 2025, the repository was transferred from https://github.com/hsimah-services to https://github.com/AxeWP/wp-graphql-facetwp.

Community contributions are welcome and encouraged.

Basic support is provided for free, both in this repo and at the #facetwp channel in WPGraphQL Discord.

Priority support and custom development is available to AxePress Development sponsors.

Usage:

  • The WPGraphQL documentation can be found here.
  • The FacetWP documentation can be found here.

Registering a facet to WPGraphQL

It is assumed that facets have been configured.

To register a FacetWP query in the WPGraphQL schema for a WordPress post type (eg post) simply call the following function:

// Register facet for Posts
add_action( 'graphql_facetwp_init', function () {
  register_graphql_facet_type( 'post' );
} );

This will create a WPGraphQL postFacet field on the RootQuery. The payload includes a collection of queried facets and a posts connection. The connection is a standard WPGraphQL connection supporting pagination and server side ordering. The connection payload only includes filtered posts.

Example query

Note This is not a complete list of GraphQL fields and types added to the schema. Please refer to the WPGraphiQL IDE for more queries and their documentation.

query GetPostsByFacet( $query: FacetQueryArgs, $after: String, $search: String, $orderBy: [PostObjectsConnectionOrderbyInput] ) {
  postFacet(
    where: { 
      status: PUBLISH,
      query: $query # The query arguments are determined by the Facet type.
    }
  ) {
    facets { # The facet configuration
      selected
      name
      label
      choices {
        value
        label
        count
      }
    }
    posts ( # The results of the facet query. Can be filtered by WPGraphQL connection where args 
      first: 10,
      after: $after,
      where: { search: $search, orderby: $orderBy} # The `orderby` arg is ignored if using the Sort facet.
    ) {
      pageInfo {
        hasNextPage
        endCursor
      }
      nodes {
        title
        excerpt
      }
    }
  }
}

WooCommerce Support

Support for WooCommerce Products can be added with following configuration:

// This is the same as all CPTs.
add_action( 'graphql_facetwp_init', function () {
  register_graphql_facet_type( 'product' );
});

// This is required because WooGQL uses a custom connection resolver.
add_filter( 'facetwp_graphql_facet_connection_config', 
  function ( array $default_graphql_config, array $facet_config ) {
    $type = $config['type'];

    $use_graphql_pagination = \WPGraphQL\FacetWP\Registry\FacetRegistry::use_graphql_pagination();

    return array_merge(
      $default_graphql_config,
      [
        'connectionArgs'    => \WPGraphQL\WooCommerce\Connection\Products::get_connection_args(),
        'resolveNode'       => function ( $node, $_args, $context ) use ( $type ) {
            return $context->get_loader( $type )->load_deferred( $node->ID );
        },
        'resolve'           => function ( $source, $args, $context, $info ) use ( $type, $use_graphql_pagination ) {
          // If we're using FWP's offset pagination, we need to override the connection args.
            if ( ! $use_graphql_pagination ) {
              $args['first'] = $source['pager']['per_page'];
            }

            $resolver = new \WPGraphQL\Data\Connection\PostObjectConnectionResolver( $source, $args, $context, $info, $type );

            // Override the connection results with the FWP results.
            if( ! empty( $source['results'] ) ) {
              $resolver->->set_query_arg( 'post__in', $source['results'] );
            }

            // Use post__in when delegating sorting to FWP.
            if ( ! empty( $source['is_sort'] ) ) {
              $resolver->set_query_arg( 'orderby', 'post__in' );
            } elseif( 'product' === $type ) {
              // If we're relying on WPGQL to sort, we need to to handle WooCommerce meta.
              $resolver = Products::set_ordering_query_args( $resolver, $args );
            }

            return $resolver ->get_connection();
        },
      ]
    );
  },
  100,
  2
);

Limitations

Currently the plugin only has been tested using Checkbox, Radio, and Sort facet types. Support for additional types is in development.

Testing

  1. Update your .env file to your testing environment specifications.
  2. Run composer install-test-env to create the test environment.
  3. Run your test suite with Codeception. E.g. vendor/bin/codecept run wpunit will run all WPUnit tests.

Releases

14 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
0.5.2 latest
Jun 7, 2025 1 year ago
wp-graphql-facetwp.zip +1 asset
33
wp-graphql-facetwp.zip27
schema.graphql6
Feb 16, 2025 1 year ago
wp-graphql-facetwp.zip +1 asset
24
wp-graphql-facetwp.zip10
schema.graphql14
Apr 6, 2024 2 years ago
wp-graphql-facetwp.zip +1 asset
54
wp-graphql-facetwp.zip26
schema.graphql28
Jun 5, 2023 3 years ago
wp-graphql-facetwp.zip +1 asset
46
wp-graphql-facetwp.zip15
schema.graphql31
May 13, 2023 3 years ago
wp-graphql-facetwp.zip +1 asset
29
wp-graphql-facetwp.zip10
schema.graphql19
May 3, 2023 3 years ago
wp-graphql-facetwp.zip +1 asset
24
wp-graphql-facetwp.zip10
schema.graphql14
Jan 12, 2023 3 years ago
wp-graphql-facetwp.zip +1 asset
46
wp-graphql-facetwp.zip11
schema.graphql35
Sep 28, 2022 4 years ago
wp-graphql-facetwp.zip +1 asset
40
wp-graphql-facetwp.zip15
schema.graphql25
Sep 28, 2022 4 years ago
tag only
—
Jun 19, 2022 4 years ago
tag only
—
Jan 4, 2021 5 years ago
tag only
—
May 26, 2020 6 years ago
tag only
—
Jul 3, 2019 7 years ago
tag only
—
Jun 14, 2019 7 years ago
tag only
—

Active-site estimate ≈10 comes from the median of recent superseded releases. Method.

B grade

Security

WPGraphQL for FacetWP 0.5.2 · audited by WP Registry

Medium-severity findings.

1 medium
Audited release
0.5.2
Findings
1
Worst severity
medium
Content hash
eae21509c7a398218ac53f5c…

Findings

  • medium GraphQL facet query forwards PostStatusEnum to FacetWP without capability checks

    missing_capability

    Once a site registers a post type with register_graphql_facet_type(), RootQuery.{type}Facet is a public GraphQL field (no auth_callback). Its resolver copies where.status into FWP()->api->process_request() as WP_Query post_status, defaulting only to 'publish' when the arg is empty. WPGraphQL's own posts connection runs the same PostStatusEnum through PostObjectConnectionResolver::sanitize_post_stati(), which allows 'publish' for everyone, 'private' only with read_private_posts, and other stati only with edit_posts. This plugin never calls that sanitizer. WordPress listing WP_Query honors an explicit post_status (draft/pending/private/future/trash/any) without mapping meta caps, so an unauthenticated or Subscriber GraphQL request can ask FacetWP to filter/count non-public posts. The posts connection still goes through WPGraphQL's Post loader/model (content remains gated), but the FacetWP payload is returned as GraphQL FacetPager (total_rows/total_pages) and Facet.choices (labels include post titles for some facet sources). Sites that index non-publish statuses via facetwp_indexer_query_args (documented FacetWP usage) increase the chance that choice labels/IDs are unpublished content.

    src/Registry/FacetRegistry.php:197-227

    Recommendation

    Sanitize where.status the same way WPGraphQL does in PostObjectConnectionResolver::sanitize_post_stati() before calling FacetWP: allow public statuses for everyone; allow private only when the current user can read_private_posts for that post type; allow draft/pending/future/trash/any only when the user can edit_posts (or read their own). Cap pager.per_page to WPGraphQL's connection max query amount. Do not run process_request for disallowed stati.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.