Recently, a WPGraphQL user asked how to query only sticky posts with a GraphQL query.
One of the great things about WPGraphQL is how customizable it is with hooks and filters, making it easy to extend the API for your needs.
End goal
One (of many possible) solutions would be to allow the client to specify that they only want sticky posts as an argument on the posts
connection query.
A query could look something like the following:
query GET_STICKY_POSTS {
posts( where: {onlySticky: true }) {
nodes {
id
title
date
link
}
}
}
This query would allow the client to specify that they want posts, onlySticky
This should give us what we were looking for, a way to query only sticky posts using WPGraphQL.
The issue is that onlySticky
Register the “onlySticky” argument
To add this field as an argument, we can use the following snippet:
add_action( 'graphql_register_types', function() {
register_graphql_field( 'RootQueryToPostConnectionWhereArgs', 'onlySticky', [
'type' => 'Boolean',
'description' => __( 'Whether to only include sticky posts', 'your-textdomain' ),
] );
} );
Here we hook into the graphql_register_types
action, to make sure the GraphQL Type registry is ready to be hooked into.
Next, we register a field to the GraphQL schema by using the register_graphql_field()
method.
The first argument is the name of the Type to register the field RootQueryToPostConnectionWhereArgs
posts
The next argument is the name of the field we’re registering. Here, we’re using onlySticky
as the field name.
The third argument is an array to configure the field. We declare the Type the field should be is Boolean, meaning it should be either true
or false
, and provide a description for the field.
At this point, our query would validate, as onlySticky
would be a valid argument on the query now, but our actual results aren’t affected.
Filter the WP_Query to respect the onlySticky input
The next step we need to take is to filter how WPGraphQL resolves the query and make sure it respects the onlySticky
argument that was input.
We can do so with the following snippet:
add_filter( 'graphql_post_object_connection_query_args', function( $query_args, $source, $args, $context, $info ) {
if ( isset( $args['where']['onlySticky'] ) && true === $args['where']['onlySticky'] ) {
$sticky_ids = get_option( 'sticky_posts' );
$query_args['posts_per_page'] = count( $sticky_ids );
$query_args['post__in'] = $sticky_ids;
}
return $query_args;
}, 10, 5 );
Here, we graphql_post_object_connection_query_args
This allows for the $query_args
that are prepared to send to WP_Query for execution to be filtered prior to WP_Query fetching the posts.
Inside this filter, we check to see if the $args
that were passed through the resolver from GraphQL include the onlySticky
input, and if that value is true
.
If those conditions are met, then we define custom $query_args, by first getting a list of the sticky posts, then asking to query only those IDs and the posts_per_page
equal to the number of sticky posts we have.
Then we return the modified $query_args
for WP_Query to use to resolve the query.
In action
Now, we can see our query in action.
First, go set a couple posts to sticky, if you haven’t already:

Then, using WPGraphiQL, execute the query, and the results returned should only be the Sticky posts!

Conclusion
My hope is that this shows how easy it is to extend WPGraphQL for your system’s needs. The plugin is powerful out of the box, but if you have custom needs for your application, take advantage of the various hooks and filters in the plugin to make it work for you!
Hey,
Thanks for the article, it is really helpful!
I think that I found 1 mistake in the query_args.
Should be key ‘post__in’ instead of ‘p’.
‘p’ accepts single post ID (type Int).
‘post__in’ accepts an array of IDs
and get_option( ‘sticky_posts’ ) returns an array
reference – https://codex.wordpress.org/Class_Reference/WP_Query#Post_.26_Page_Parameters
so should be:
$query_args = [
‘posts_per_page’ => count( $sticky_ids ),
‘post__in’ => $sticky_ids
];
Hey! Good catch. The actual demo code used to make the screen recording had post__in, not sure how it made it to the snippet as just āpā.
Got it updated. ????
Hi Jason, this is really great! ???? How can I use this with custom post types?
Hey!
So to apply to custom post types, you would just need to adjust the name of the connection you’re registering the `onlySticky` field to.
So instead of `RootQueryToPostConnectionWhereArgs` it would be:
‘RootQueryTo’ . $post_type->graphql_single_name . ‘ConnectionWhereArgs’
So you could do something like:
$allowed_post_types = \WPGraphQL::$allowed_post_types;
foreach ( $allowed_post_types as $post_type ) {
// put the field registration here
}
Thanks for the article
How would I apply the filter hook on custom post type
Here is my code:
add_action( ‘graphql_register_types’, function() {
register_graphql_field( ‘RootQueryToListingConnectionWhereArgs’,
‘l_category’, [
‘type’ => ‘String’,
‘description’ => ‘list category’,
]);
});
the bellow filter isn’t working
add_filter( ‘graphql_listing_object_connection_query_args’,
function($query_args, $source, $args, $context, $info){
if(if ( isset( $args[‘where’][‘l_category’] ) ){
$query_args = array(‘tax_query’=> array(
‘taxonomy’ => ‘l_category’,
‘field’ => ‘term_id’,
‘terms’ => $args[‘where’][‘l_category’] ,
));
}
return $query_args;
}, 10, 5 );
I believe the filter you need is `graphql_post_object_connection_query_args `
See: https://github.com/wp-graphql/wp-graphql/blob/develop/src/Data/Connection/PostObjectConnectionResolver.php#L306
Before trying to get this to work on a custom post type I’m trying to get this exact code to work on my site, but all I get is in WPgraphiQL
“`
{
“errors”: [
{
“message”: “Internal server error”,
“category”: “internal”,
“locations”: [
{
“line”: 2,
“column”: 3
}
],
“path”: [
“posts”
]
}
],
“data”: {
“posts”: null
}
}
“`
If I log `$query_args` in `graphql_post_object_connection_query_args` I get one post back and it seems to look fine
“`
[14-Apr-2020 12:32:03 UTC] Array
(
[posts_per_page] => 1
[post__in] => Array
(
[0] => 1059
)
)
“`
Thanks for reporting this. I’ve updated the post with a correct snippet filter. Instead of the filter overriding the $query_args, it should add to them.
Before (errors):
“`
add_filter( ‘graphql_post_object_connection_query_args’, function( $query_args, $source, $args, $context, $info ) {
if ( isset( $args[‘where’][‘onlySticky’] ) && true === $args[‘where’][‘onlySticky’] ) {
$sticky_ids = get_option( ‘sticky_posts’ );
$query_args = [
‘posts_per_page’ => count( $sticky_ids ),
‘post__in’ => $sticky_ids,
];
}
return $query_args;
}, 10, 5 );
“`
After (working):
“`
add_filter( ‘graphql_post_object_connection_query_args’, function( $query_args, $source, $args, $context, $info ) {
if ( isset( $args[‘where’][‘onlySticky’] ) && true === $args[‘where’][‘onlySticky’] ) {
$sticky_ids = get_option( ‘sticky_posts’ );
$query_args[‘posts_per_page’] = count( $sticky_ids );
$query_args[‘post__in’] = $sticky_ids;
}
return $query_args;
}, 10, 5 );
“`