Skip to content

Instantly share code, notes, and snippets.

View jasonbahl's full-sized avatar
:octocat:

Jason Bahl jasonbahl

:octocat:
View GitHub Profile
@jasonbahl
jasonbahl / README.md
Last active September 25, 2026 19:53
On-demand revalidation for a headless front end, driven by WPGraphQL Smart Cache purge events

On-demand revalidation from WPGraphQL Smart Cache purge events

Tells a decoupled front end to rebuild affected pages when content changes, by listening to the purge events WPGraphQL Smart Cache already emits.

Used in production on the CMS behind wpgraphql.com.

The problem

A front end using ISR (Next.js, or anything with a revalidate window) serves a

@jasonbahl
jasonbahl / README.md
Created September 24, 2026 20:05
Post Types Order + WPGraphQL ordering bridge, fixes filter registration timing and orderby array handling

Post Types Order + WPGraphQL ordering bridge

Post Types Order hooks posts_orderby at priority 99 and rewrites the ORDER BY SQL directly, which breaks WPGraphQL's cursor pagination. This drops that filter for GraphQL requests and expresses the manual order through WP_Query args instead, where WPGraphQL can see it and paginate correctly.

This replaces an earlier version of this snippet from 2022. That one works for the case it was written for, but it has three problems that took a while to track down on a real site.

@jasonbahl
jasonbahl / README.md
Created September 24, 2026 20:02
Headless mode for WordPress: 301 the front end to your decoupled site, keep REST and GraphQL working

Headless mode for WordPress: redirect the front end, keep the APIs

Two small pieces that let a WordPress install act purely as a headless backend, used in production on the CMS behind wpgraphql.com.

  1. Stop serving front-end templates. Every front-end request 301s to the decoupled front end. REST, GraphQL, cron, WP-CLI and wp-admin are untouched.
  2. Point home_url() at the front end, so plugins that build URLs from it (Yoast in particular) produce front-end URLs rather than CMS ones.
@jasonbahl
jasonbahl / Invalidation.php
Created September 10, 2026 20:46
WPGraphQL Smart Cache: purge webhooks / on-demand revalidation proof of concept (from wp-graphql-smart-cache#287)
<?php
namespace WPGraphQL\SmartCache\Cache;
use Exception;
use GraphQLRelay\Relay;
use WP_Comment;
use WP_Post;
use WP_Term;
use WP_User;
use WPGraphQL\Model\Menu;
@jasonbahl
jasonbahl / security-fix.patch
Created December 11, 2025 19:55
Fix for authenticated cache leak vulnerability
diff --git a/src/Cache/Query.php b/src/Cache/Query.php
index ec5cfdc..3435ac0 100644
--- a/src/Cache/Query.php
+++ b/src/Cache/Query.php
@@ -22,6 +22,13 @@ class Query {
**/
public static $storage = null;
+ /**
+ * The current GraphQL request.
@jasonbahl
jasonbahl / security-fix.patch
Last active December 11, 2025 19:44
Fix for authenticated cache leak vulnerability
diff --git a/src/Cache/Query.php b/src/Cache/Query.php
index ec5cfdc..3435ac0 100644
--- a/src/Cache/Query.php
+++ b/src/Cache/Query.php
@@ -22,6 +22,13 @@ class Query {
**/
public static $storage = null;
+ /**
+ * The current GraphQL request.
import { GraphQLClient, gql } from 'graphql-request';
import { SEED_QUERY } from '@/components/WPTemplateRouter/SEED_QUERY';
import templates from '@/wp-templates/wp-templates.js';
/**
* Fetches the seed node data from WordPress using the SEED_QUERY.
*
* @param {object} context - The context object.
* @param {string} context.uri - The URI of the node to fetch.
* @param {boolean} [context.isPreview=false] - Whether this is a preview request.
<?php
/**
* Plugin Name: WPGraphQL Track GetText
* Description: Test plugin for tracking the number of times the wp-graphql textdomain is translated during a WPGraphQL request. The count is output in the "extensions" portion of the graphql response.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
@jasonbahl
jasonbahl / wp-graphql-page-siblings-connection.php
Created July 13, 2023 14:47
Register a page siblings connection in WPGraphQL
add_action( 'graphql_register_types', function() {
register_graphql_connection( [
'fromType' => 'Page',
'toType' => 'Page',
'connectionTypeName' => 'PageSiblings',
'fromFieldName' => 'siblings',
'resolve' => function( $page, $args, $context, $info ) {
$parent = $page->parentDatabaseId ?? null;
@jasonbahl
jasonbahl / wp-graphql-smart-cache-custom-even-listener.php
Created June 1, 2023 16:33
Listen to custom events and call purge
add_action( 'graphql_cache_invalidation_init', static function( \WPGraphQL\SmartCache\Cache\Invalidation $invalidation ) {
add_action( 'updated_option', static function( $option, $value, $original_value ) use ( $invalidation ) {
// phpcs:ignore
if ( ! isset( $_POST['_acf_screen'] ) || 'options' !== $_POST['_acf_screen'] ) {
return;
}
// phpcs:ignore