Skip to content

Instantly share code, notes, and snippets.

@petertenhoor
Created February 19, 2019 21:42
Show Gist options
  • Select an option

  • Save petertenhoor/4d1c1f80fde794214fc2f1917013340f to your computer and use it in GitHub Desktop.

Select an option

Save petertenhoor/4d1c1f80fde794214fc2f1917013340f to your computer and use it in GitHub Desktop.
WP GraphQL serverside caching example
<?php
namespace PTH;
/**
* Class GraphqlCacheComponent
*
* @package PTH
*/
class GraphqlCacheComponent extends Singleton
{
/**
* Define cache constants
*/
const CACHE_DURATION_SECONDS = 0;
const CACHE_GROUP_NAME = 'wpgraphql_cache';
const CACHE_HEADER_NAME_SERVER = 'HTTP_X_GRAPHQL_CACHE';
const CACHE_HEADER_NAME_CLIENT = 'x-graphql-cache';
const CACHE_STATUS_HEADER_NAME = 'wp-graphql-cache-status:';
/**
* GraphqlCacheComponent constructor.
*/
protected function __construct()
{
add_action('do_graphql_request', [$this, 'handleGraphqlRequest']);
add_action('graphql_return_response', [$this, 'handleGraphqlResponse']);
add_filter('graphql_access_control_allow_headers', [$this, 'allowCustomHeader']);
}
/**
* Handle graphql request
*/
public function handleGraphqlRequest()
{
//get cache key from headers
$cacheKey = self::getCacheKey();
//return if no cache key found in headers
if ($cacheKey === false) return;
//get cached data from server
$cachedData = wp_cache_get($cacheKey, self::CACHE_GROUP_NAME);
if ($cachedData !== false) {
header('Content-Type: application/json');
header(self::CACHE_STATUS_HEADER_NAME . 'hit');
header('Access-Control-Allow-Headers: Content-Type');
header("Access-Control-Allow-Origin: *");
echo $cachedData;
die();
}
//we missed, set header
header(self::CACHE_STATUS_HEADER_NAME . 'miss');
}
/**
* Handle graphqpl response
*
* @param $response
*/
public function handleGraphqlResponse($response)
{
//get cache key from headers
$cacheKey = self::getCacheKey();
//return if no cache key found in headers
if ($cacheKey === false) return;
//add graphql response to the cache if it has no errors
if (property_exists($response, "errors") && count($response->errors) === 0) {
wp_cache_add($cacheKey, wp_json_encode($response->toArray()), self::CACHE_GROUP_NAME, self::CACHE_DURATION_SECONDS);
}
}
/**
* Add custom header to allowed headers
*
* @param $headers
* @return array
*/
public static function allowCustomHeader($headers)
{
$headers[] = self::CACHE_HEADER_NAME_CLIENT;
return $headers;
}
/**
* Get cache key
*
* @return string|bool
*/
public static function getCacheKey()
{
return isset($_SERVER[self::CACHE_HEADER_NAME_SERVER]) ? $_SERVER[self::CACHE_HEADER_NAME_SERVER] : false;
}
}
import {ApolloClient, InMemoryCache, HttpLink} from 'apollo-boost'
import fetch from "isomorphic-fetch";
import {Base64} from 'js-base64'
let apolloClient = null
function create(initialState) {
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser,
link: new HttpLink({
fetch: customFetch,
uri: "xxx",
credentials: 'same-origin'
}),
cache: new InMemoryCache({
dataIdFromObject: obj => obj.id,
addTypename: false,
fragmentMatcher: {
match: ({id}, typeCond, context) => !!context.store.get(id)
}
}).restore(initialState || {})
})
}
/**
* Custom fetch (add header for caching)
*
* @param uri
* @param options
* @returns {*}
*/
const customFetch = (uri, options) => {
const cacheKey = Base64.encode(JSON.stringify(options.body))
options.headers['x-graphql-cache'] = cacheKey
console.log('fetching with key', cacheKey)
return fetch(uri, options)
}
export default function initApollo(initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment