WordPress REST API in the Wild: Cache Clearing

In the last few weeks, I’ve been doing a lot of work integrating & automating some of the systems we use at Actionable.co. This week, I found an interesting use for the WordPress REST API that I thought I’d share.

We have a web page on one of our public sites that shows the names, locations and headshots of our consultant network. This data does not originate in WordPress, but in a separate platform. I make a REST call to that platform to retrieve the most up-to-date information to display on this page. For reasons of performance, I cache this data. Until this week, I had instructed WordPress to keep this data for 24 hours.

Here’s a snippet of that code (originated in a class, which is why you’ll see $this used.

1 2 3 4 5 6 7 8 9 10 11 12 // Retrieve Consultant List from Cache $this -> consultantJsonData = wp_cache_get ( 'active_consultant_list' , 'gem_acp_network_data' ) ; // If no cached version exists, retrieve consultant list from the // Conversations API, and then cache it for future use. if ( $this -> consultantJsonData === false ) { $response = wp_remote_get ( 'https://example.com/api/Consultants/consultantList' ) ; if ( is_array ( $response ) ) { $this -> consultantJsonData = $response [ 'body' ] ; // use the content wp_cache_set ( 'active_consultant_list' , $this -> consultantJsonData , 'gem_acp_network_data' , DAY_IN_SECONDS ) ; } } // Retrieve Consultant List from Cache $this->consultantJsonData = wp_cache_get( 'active_consultant_list', 'gem_acp_network_data' ); // If no cached version exists, retrieve consultant list from the // Conversations API, and then cache it for future use. if ( $this->consultantJsonData === false ) { $response = wp_remote_get( 'https://example.com/api/Consultants/consultantList' ); if ( is_array( $response ) ) { $this->consultantJsonData = $response['body']; // use the content wp_cache_set( 'active_consultant_list', $this->consultantJsonData, 'gem_acp_network_data', DAY_IN_SECONDS ); } }

It worked great! Once a day, this page would get updated with the most recent headshots.

But I really wanted it to be more real-time than that… I wanted to clear the cache whenever we added or removed a consultant from our network, or when their information was updated. My current solution (if I was the one doing the updates) was to SSH into the website and clear the cache key using WP-CLI. It worked, but it was an added few steps that took time. There had to be a better way!

Enter the REST API….

On the public website, I registered a new endpoint that would clear the cache.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // Register Endpoint to Clear ACP Network Data add_action ( 'rest_api_init' , array ( $this , 'rest_api_clear_acp_network_data' ) ) ; /*** * Registers a new endpoint in the REST API to clear the ACP * Network Cache. * * @return void */ public function rest_api_clear_acp_network_data ( ) { register_rest_route ( 'acp-network-data/v1' , '/clearCache' , array ( 'methods' => 'POST' , 'callback' => array ( $this , 'clear_consultant_cache' ) , 'permission_callback' => 'is_user_logged_in' , ) ) ; } /*** * Deletes the 'active_consultant_list' cache key * * @return bool */ public function clear_consultant_cache ( ) { return array ( 'success' => wp_cache_delete ( 'active_consultant_list' , 'gem_acp_network_data' ) ) ; } // Register Endpoint to Clear ACP Network Data add_action( 'rest_api_init', array( $this, 'rest_api_clear_acp_network_data' ) ); /*** * Registers a new endpoint in the REST API to clear the ACP * Network Cache. * * @return void */ public function rest_api_clear_acp_network_data() { register_rest_route( 'acp-network-data/v1', '/clearCache', array( 'methods' => 'POST', 'callback' => array( $this, 'clear_consultant_cache'), 'permission_callback' => 'is_user_logged_in', ) ); } /*** * Deletes the 'active_consultant_list' cache key * * @return bool */ public function clear_consultant_cache() { return array( 'success' => wp_cache_delete( 'active_consultant_list', 'gem_acp_network_data' ) ); }

UPDATE: Notice that I’m using the permission_callback argument to make sure that we’re dealing with an authenticated user. I don’t want anonymous users to be able to call the REST endpoint and continuously clear my cache. I had originally been doing this in the clear_consultant_cache function, but the permission_callback is a much nicer way. Thank you Pete Nelson for the tip! (nothing like free code review!)

Then, in our internal management tools, I can call this endpoint when I want the page to be refreshed. For example, when I update a consultant’s headshot.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 /*** * Updates the a consultant's headshot * * @param int $consultant_id The ID of the consultant * @param string $headshot_url The URL to the new headshot * * @return bool|WP_Error */ public function update_consultant_headshot ( $consultant_id , $headshot_url ) { // Update the Headshot URL in the database global $wpdb ; $wpdb -> update ( $wpdb -> prefix . 'consultant' , array ( 'headshotUrl' => $headshot_url ) , array ( 'platformConsultantId' => $consultant_id ) , array ( '%s' ) , array ( '%d' ) ) ; // Clear the public website ACP Network Data Cache $args = array ( 'headers' => array ( 'Authorization' => 'Bearer ' . JWT_DSM_TOKEN , ) ) ; $response = wp_safe_remote_post ( $url , $args ) ; if ( is_wp_error ( $response ) ) { return $response ; } return true ; } /*** * Updates the a consultant's headshot * * @param int $consultant_id The ID of the consultant * @param string $headshot_url The URL to the new headshot * * @return bool|WP_Error */ public function update_consultant_headshot( $consultant_id, $headshot_url ) { // Update the Headshot URL in the database global $wpdb; $wpdb->update( $wpdb->prefix . 'consultant', array( 'headshotUrl' => $headshot_url ), array( 'platformConsultantId' => $consultant_id ), array( '%s' ), array( '%d' ) ); // Clear the public website ACP Network Data Cache $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . JWT_DSM_TOKEN, ) ); $response = wp_safe_remote_post( $url, $args ); if ( is_wp_error( $response ) ) { return $response; } return true; }

You’ll notice I’m passing in an Authorization header. My REST API implementation is using JSON Web Tokens (JWT) between sites. They’re simple to setup using the JWT Authentication for WP REST API plugin.

Cleanup

Boom! Now the public website is kept up-to-date in real-time, while still making use of the cache when the data hasn’t changed.

One last change, in the code where I retrieve the consultant list, I was previously storing the cache for DAY_IN_SECONDS , which would force it to be refreshed once a day. After implementing the endpoint above, I can now change this to 0 , which keeps the data cached as long as possible. The end result is that I’m now making more efficient use of the cache, as in many cases, deleting this cache key after 24 hours wasn’t required.