API Fetch – The WordPress Library You Didn’t Know You Needed

One of my favorite WordPress packages that nobody seems to talk about right now is @wordpress/apiFetch. This library works kind-of like your traditional fetch library, only it’s built into WordPress, plays nice with backwards compatibility, and is capable of using middleware to transform some, or all of the REST API calls that it receives.

There are many people who go fully headless with GraphQL, Gatsby and the like, but I prefer a semi-headless approach to WordPress. This allows me to use WordPress plugins to their full potential, but still make use of the REST API when I can. One of the key pieces of this approach is making extensive use of the apiFetch library.

Setting Up api-fetch

api-fetch is a built-in package that you can install just like any other NPM package, but it’s best to instead require the packaged up API Fetch library as a dependency. This will minimize conflicts with other plugins that are also using api-fetch.

To use it, you just need to add it to your list of dependencies on your registered script. If you’re using Underpin, that would look like this:

plugin_name()->scripts()->add( 'test', [
        'handle'      => 'test',
        'src'         => 'path/to/script/src',
        'name'        => 'test',
        'description' => 'The description',
    'deps'        => [ 'wp-api-fetch', 'wp-polyfill' ],
        'middlewares' => [
          'Underpin_Scripts\Factories\Enqueue_Script'
        ]
] );

If you wanted to-do this without Underpin, it would look more like this:

wp_register_script( 'test', 'path/to/script/src', ['wp-api-fetch', 'wp-polyfill'] );

add_action( 'wp_enqueue_scripts', function(){
    wp_enqueue_script( 'test' );
} );

This is okay, but it’s way better If you’re extending wordpress/scripts‘ Webpack configuration. This is because you can actually use import statements inside your Javascript for any @wordpress package, just like as-if you had installed the NPM package.

WordPress has a truly magical Webpack loader that will actually extract the WordPress-specific dependencies, and generate the dependencies array as a PHP file. You can then require this file, and it will automatically set your script dependencies for you. That way you can just write your JS as you were, and it will automatically update the dependency array for you.

This is way easier once you get it set-up. You can see how Underpin sets up a Webpack config for these sorts of things in Underpin’s plugin boilerplate.

Once you get that all-set, you can instead use this file to set dependencies. With Underpin, that would look like this:

plugin_name()->scripts()->add( 'test', [
        'handle'      => 'test',
        'src'         => 'path/to/script/src',
        'name'        => 'test',
        'description' => 'The description',
    'deps'        => plugin_name()->dir() . 'build/test.asset.php', // path to dependency file generated by webpack
        'middlewares' => [
          'Underpin_Scripts\Factories\Enqueue_Script' // Enqueue the script on the front-end
        ]
] );

Without Underpin, it can be done but requires a bit of extra logic:

// Check to see if the file exists.
$deps_file = plugin_dir_path(__FILE__) . 'path/to/file';

// Set default fallback to dependencies array
$deps = [];

// If the file can be found, use it to set the dependencies array.
if ( file_exists( $deps_file ) ) {
    $deps_file = require( $deps_file );
    $deps      = $file['dependencies'];
}

// Register script
wp_register_script( 'test', 'path/to/script/src', $deps );

// Enqueue the script later-on
add_action( 'wp_enqueue_scripts', function(){
    wp_enqueue_script( 'test' );
} );

Using api-fetch

Like I mentioned earlier, API fetch works a lot like the regular fetch API, but the syntax is a little different. Let’s say you wanted to get a list of posts via REST. It would look something like this:

apiFetch( {
    path: '/wp/v2/posts'
} );

This would return aJavascript Promise, much like how the normal Fetch API, however, unlike Fetch, this will return a parsed object instead of a response object after the promise resolves.

The example above doesn’t do anything with the object, however. There’s two primary methods to use the response for this. One is with the then statement, or async/await. Then is a little simpler to set up, but I find that async/await is easier to read.

Using then:

apiFetch( {
    path: '/wp/v2/posts'
} ).then( ( posts ) => /** do something with the posts **/ );

Using async/await:

const posts = await apiFetch( {
    path: '/wp/v2/posts'
} );

// Do something with your posts

Middleware

Okay, so this is all fine and dandy, but what’s the point of using this library when you can just use fetch and accomplish the same thing? The answer? Middleware.

apiFetch middleware allows you to automatically run a callback function, and mutate the request on every apiFetch call. This allows you to do some really cool things. api-fetch comes with three “built-in” middlewares that you can set up using a method.

  1. createRootURLMiddleware will Automatically set the root REST API url so you only have to pass the relative path in your requests. This is important because the REST API URL can differ from WordPress install to WordPress install (don’t get me started with multisite)
  2. createNonceMiddleware allows you to automatically authenticate a logged in user with a nonce.
  3. createPreloadingMiddleware will allow you to preload REST API requests so that they load instantly instead of making another request on the server when that request is called on the front-end. (More on this later)

These can be set up inside Javascript directly, but it is common to load these as an inline script just after the script is enqueued using wp_add_inline_script, like so:

// Add a nonce to all requests
wp_add_inline_script(
    'test' // Script handle to add after
    'apiFetch.use( apiFetch.createNonceMiddleware( '. wp_create_nonce( 'wp_rest' ) .' ) )', // Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.
);

// Set the root URL for all requests
wp_add_inline_script(
    'test' // Script handle to add after
    'apiFetch.use( apiFetch.createRootURLMiddleware( '. get_rest_url() .' ) )', // Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.
);

Preloading Middleware

The first two built-in middlewares have a fairly obvious use-case – most REST requests need at least one of these in-order for the site to work consistently. Prior to middleware, this was done using wp_localize_script, and even at that time it was necessary to pass one, or both of these things in every request.

Preloading, however, is a bit different, and is defionitely not something that should be used in most cases, but there are times where it makes a lot of sense to use.

What does Preloading Middleware do?

Preloading middleware accepts a Javascript object where the object keys are specific WordPress REST API endpoints, and the values are the corresponding result of that particular endpoint. When this middleware is set, apiFetch will automatically check to see if the provided REST endpoint has been pre-loaded. If it has, it will use the preloaded response instead of making another fetch request.

When Should This Be Used?

I find the best time to use this feature is when you have had to retrieve the same data on the server as what will need to be retrieved on the actual site. For example – a WordPress request on the front-end always runs the page’s query when loading the content. If you’re using a semi-headless approach, and are ultimately using REST to fetch the post content after the site is loaded, you’re essentially forcing WordPress to fetch that data from the database twice. That kind-of sucks!

But you can, however, use a method similar to what we did above to preload the REST API data that has already been retrieved by the database. Something like this will take advantage of the object cache, and allow your web app to instantly have access to the posts instead of waiting for another request to complete. It’s a win-win because you’re taking advantage of data that has already been retrieved from the database, and reducing your requests on page load.

add_action('wp_enqueue_scripts', function(){
    // Preload the fetch data

    $current_page = get_query_var( 'paged' ) ?? 1; // Get the current page, and use page 1 as the default.
    $request = new \WP_REST_Request( $type, $endpoint ); // Instantiate a fake REST request.
    $request->set_query_params( ['page' => $current_page ); // Set current page
    $response = rest_do_request( $request ); // Run the REST request.

    $preloaded_data = [
        'wp/v2/posts?page=' . $current_page => $response->get_data()
    ];

    wp_add_inline_script(
        'test' // Script handle to add after
        'apiFetch.use( apiFetch.createPreloadingMiddleware( '. wp_json_encode( $preloaded_data ) .' ) )', // Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.
    );
} );

Make Your Own Middleware

The sky is the limit with this. In some plugins, I’ve loaded in my own custom cache solution that automatically caches the REST API response, passed-in required params for different authentication methods, all kinds of things. Creating your own middleware is as simple as doing something like this:

apiFetch.use( ( options, next ) => {
    // Do things, like manipulate the provided options
    return next( options );
} );

This could be done either in your Javascript, or inside of PHP using the method detailed with other middlewares above.

Conclusion

api-fetch is an awesome little fetch utility that I use all the time when working in a RESTful manner with WordPress. It’s a powerful utility that has been tried and tested on tons and tons of sites, on different browsers, and in different contexts. When used correctly, it can make working with WordPress’s REST API a lot easier. When I look at this method, and reflect on the times before the REST API, I can’t help but chuckle. It’s so much easier now, provided you know how it works.

Looking for more WordPress Resources?

Join WP Dev Academy’s Discord server, and become a part of a growing community of WordPress developers.

21