Current WordPress Rest API extension list

If you want to have all the endpoints as plugin, there is one in the plugin directory: WUXT Headless WordPress API Extensions

I love the WordPress Rest API and switching more and more from theme development to a headless WP approach, with an nice front-end framework. Right now I’m favoring Nuxt.js, which is build on Vue.js (check out wuxt, my very own dockerized nuxt/wp development environment).

For using WPs full strength with the Rest API I’ve collected/build a useful snippet library with WordPress Rest API extensions. I’ll try to maintain the following list as development goes on. All of the following extensions can be embedded in the functions.php file. If you wondering about the wuxt_ prefix, I’ve got the code from my Wuxt project and the prefix is as good as anyone.

Front-page extension

Everything starts with a nice front-page, but there no obvious way to get the WordPress front-page via the Rest API. To read the settings, you have to be authorized, which makes things unnecessary complicated. So here a custom endpoint for getting the front-page.

GET: /wp-json/wp/v2/front-page

<?php

/**
 * Adds a front-page endpoint for generell front-page settings in the
 * Front-end
 */
add_action( 'rest_api_init', 'wuxt_front_page_route' );


function wuxt_front_page_route() {
    register_rest_route( 'wp', '/v2/front-page', array(
        'methods'  => 'GET',
        'callback' => 'wuxt_get_front_page'
    ) );
}


function wuxt_get_front_page( $object ) {

    $request  = new WP_REST_Request( 'GET', '/wp/v2/posts' );

    $frontpage_id = get_option( 'page_on_front' );
    if ( $frontpage_id ) {
      $request  = new WP_REST_Request( 'GET', '/wp/v2/pages/' . $frontpage_id );
    }

    $response = rest_do_request( $request );
    if ($response->is_error()) {
        return new WP_Error( 'wuxt_request_error', __( 'Request Error' ), array( 'status' => 500 ) );
    }

    $embed = $object->get_param( '_embed' ) !== NULL;
    $data = rest_get_server()->response_to_data( $response, $embed );

    return $data;

}

?>

Right now, there is no way I know of for getting menus from the WordPress Rest API. I’m not sure if it’s the most effective way, but here my WordPress Rest API custom endpoint for menus. It registers even a standard ‘main’ menu as default if no location is requested.

Note: The most work for this snippet is done by the menu-class of Michael Cox, you have to include it to get the endpoint to work.

GET: /wp-json/wp/v2/menu?location=<location>

<?php

/**
 * Adds a menu endpoint
 */
require_once('/path/to/Menu.php');

add_action('init', 'wuxt_register_menu');
add_action('rest_api_init', 'wuxt_route_menu');

function wuxt_register_menu()
{
    register_nav_menu('main', __('Main meny'));
}

function wuxt_route_menu()
{
    register_rest_route('wp', '/v2/menu', array(
        'methods' => 'GET',
        'callback' => 'wuxt_get_menu',
    ));
}

function wuxt_get_menu($params)
{
    $params = $params->get_params();
    $theme_locations = get_nav_menu_locations();

    if (!isset($params['location'])) {
        $params['location'] = 'main';
    }

    if ( ! isset( $theme_locations[$params['location']] ) ) {
        return new WP_Error( 'wuxt_menu_error', __( 'Menu location does not exist' ), array( 'status' => 404 ) );
    }

    $menu_obj = get_term( $theme_locations[$params['location']], 'nav_menu' );
    $menu_name = $menu_obj->name;
    $menu = new Menu( $menu_name );
    return $menu->getTree();
}

?>

Filtering Categories and taxonomies

When filtering taxonomies with an Rest API request, you are stuck with OR-queries, because the category endpoint doesn’t give you the full complexity of a tax_query. That means you can get posts which are either in category A or B. The following adjustment doesn’t give you the full complexity either, but it lets you switch all tax_queries to an AND-relation, so that you can select posts which are both in category A and B.

GET: /wp-json/wp/v2/posts/?categories=1,2&and=true

<?php

  /**
   * Ads AND relation on rest category filter queries
   */
  add_action( 'pre_get_posts', 'wuxt_override_relation' );

  function wuxt_override_relation( $query ) {

    // bail early when not a rest request
  	if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
  		return;
  	}

    // check if we want to force an "and" relation
    if ( ! isset( $_GET['and'] ) || !$_GET['and'] || 'false' === $_GET['and'] || !is_array( $tax_query = $query->get( 'tax_query' ) ) ) {
  		return;
  	}

    foreach ( $tax_query as $index => $tax ) {
      $tax_query[$index]['operator'] = 'AND';
    }

  	$query->set( 'tax_query', $tax_query );

  }

?>

Loading ACF Meta-fields

Integrating meta-fields from the Advanced-Custom-Fields plugin into the Rest API responses can be done with this plugin. If you need a simpler solution (only integrating meta fields into post-objects, not writing them), or simply a bit more control, the following snippet can get you started. It sets all ACF-fields to show_in_rest, which lets them appear in the post-objects meta-section:

<?php

    /**
     * Register meta fields from ACF
     */
    add_action( 'init', 'wuxt_register_acf_meta' );

    function wuxt_register_acf_meta() {

        if( function_exists( 'acf_get_field_groups' ) ){
            $result = array();
            $acf_field_groups = acf_get_field_groups();
            foreach( $acf_field_groups as $acf_field_group) {
                foreach($acf_field_group['location'] as $group_locations) {
                    foreach($group_locations as $rule) {
                        foreach(acf_get_fields( $acf_field_group ) as $field) {
                            register_meta( 'post', $field['name'], array( 'show_in_rest' => true ) );
                        }
                    }

                }

            }
        }

    }

?>

SEO-Data

The register_meta trick is handy, even for other plugins. If you want to integrate data from our favorite SEO add-on, Yoast WordPress SEO, into the post objects, you can do it like that:

<?php

    /**
     * Register meta fields for WordPress SEO
     */
    add_action( 'init', 'wuxt_register_yoast_meta' );

    function wuxt_register_yoast_meta() {
      if(in_array('wordpress-seo/wp-seo.php', apply_filters('active_plugins', get_option('active_plugins')))){

          $allowed_yoast_keywords = array(
              '_yoast_wpseo_title',
              '_yoast_wpseo_bctitle',
              '_yoast_wpseo_metadesc',
              '_yoast_wpseo_focuskw',
              '_yoast_wpseo_meta-robots-noindex',
              '_yoast_wpseo_meta-robots-nofollow',
              '_yoast_wpseo_meta-robots-adv',
              '_yoast_wpseo_canonical',
              '_yoast_wpseo_redirect',
              '_yoast_wpseo_opengraph-description',
          );

          foreach( $allowed_yoast_keywords as $field) {
              register_meta( 'post', $field, array( 'show_in_rest' => true ) );
          }

      }
    }

?>

Building Urls

If you are building a front-end app on top of WordPress, you have to think about how to structure your urls. WordPress has two default post-types (posts & pages) and in the urls is not distinguished which type you are requesting, so http://wp-site.expl/something might lead to a page or a post, dependent on the type of the object with the slug something.

That means, that if you want to mirror that behaviour in your app, you have to do two requests for each url, one searching pages, one searching posts. To make that one request, use the following.

GET: /wp-json/wp/v2/slug/<post-or-page-slug>

<?php

/**
 * Adds a slug endpoint for getting the page or post for a given slug
 */
add_action('rest_api_init', 'wuxt_slug_route');


function wuxt_slug_route()
{
    register_rest_route('wp', '/v2/slug/(?P<slug>\S+)', array(
        'methods'  => 'GET',
        'callback' => 'wuxt_get_slug'
    ));
}


function wuxt_get_slug($object)
{

    $slug = $object->get_param('slug');

    $request = new WP_REST_Request('GET', '/wp/v2/posts');
    $request->set_param('slug', $slug);

    $response = rest_do_request($request);

    if (!$response->data) {

        $request = new WP_REST_Request('GET', '/wp/v2/pages');
        $request->set_param('slug', $slug);

        $response = rest_do_request($request);
    }

    if (!$response->data) {
        return new WP_Error('wuxt_no_such_slug', __('Slug does not exist'), array('status' => 404));
    }

    $embed = $object->get_param('_embed') !== NULL;
    $data = rest_get_server()->response_to_data($response, $embed);

    return $data[0];
}

?>

More to come …

Hope you found something of the code above useful. Send me your extensions in the comments and I will happily integrate them!

When I’m running WordPress inside a docker container, I usually only mirror the wp-content directory from the container to the host. You shouldn’t change anything in the other WordPress directories anyway and you avoid many volume syncs. The problem with this approach is that you can’t run the wp-command from the host, you have to run WP-CLI inside Docker, that means logging in to the container running the commands and logging out again. Assuming your container is called wp-container the following commands would do that:

  
docker exec -ti wp-container bash
wp plugin list
exit
  

It’s just a little inconvenience, but why not save some command when we can?

I’m using gulp as task-manager for most of my projects, and added a task which forwards all commands to WP-CLI inside Docker. It even checks if the container is running and if WP-CLI is installed inside the container. If not it will be automatically installed.

var gulp = require('gulp');
var spawn = require('child_process').spawn;

var checkContainers = function(names, done) {
  var exec = require('child_process').exec;
  exec('docker ps --format {{.Names}}', function(error, stdout, stderr) {
    done(names.map(function(name) {
      return stdout.split("\n").indexOf(name) >= 0;
    }).reduce(function(running, next) {
      return running && next;
    }, true));
  });
};

var checkWPCli = function(container, done) {
  var exec = require('child_process').exec;
  exec('docker exec ' + container + ' bash -c \'wp\'', function(error, stdout, stderr) {}).on('exit', function(code) {
    done(127 !== code);
  });
};

var installWPCli = function(container, done) {
  var exec = require('child_process').exec;
  exec('docker exec ' + container + ' bash -c \'apt-get update && apt-get install -y less && curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp && wp --allow-root cli\'', function(error, stdout, stderr) {}).on('exit', function(code) {
    done(0 === code);
  });
};

var runWPCli = function(container, done) {
  var command = process.argv.slice(process.argv.indexOf('-c') + 1);

  if (!command.length) {
    console.log('ERROR: Provide a valid wp-cli command!');
    return done();
  }

  var exec = require('child_process').exec;
  exec('docker exec ' + container + ' bash -c \'wp --allow-root ' + command.join(' ') + '\'', function(error, stdout, stderr) {
    console.log(stdout);
  }).on('exit', function(code) {
    done();
  });
};

gulp.task( 'wp', function(done) {

  checkContainers(['wp-container'], function(containerRunning) {

    if (!containerRunning) {
      console.log('ERROR: wp-container container is not running. Try "docker-compose up -d"')
      return done();
    }

    checkWPCli('wp-container', function(wpCliRunning) {
      if (!wpCliRunning) {

        console.log('WARNING: wp cli not installed, trying auto install ...');

        installWPCli('wp-container', function(wpCliRunning) {

          console.log('SUCCESS: wp cli installed!');
          runWPCli('wp-container', done);

        });
      } else {

        runWPCli('wp-container', done);

      }
    });
  });

});

So, with that gulp task in place, you will be able to run the following commands and every other WP-CLI command as well:

gulp wp -c "plugin list"
gulp wp -c "plugin install advanced-custom-fields"

What do you think?

Is that to much hassle for running some wp-commands or would you use the gulp forward in your daily work? Let me know!

In the last couple of months I’ve worked a great deal with WordPress as backend system, providing data to a JavaScript frontend via the Rest API. As I couldn’t find a complete solution for my favorite frontend-framework, my “getting started” process always repeated the following steps

  • set up WordPress development environment (docker)
  • integrate Javascript frontend in the code
  • setup environment for the frontend (docker)
  • make the two environments talk to each other (not always as easy as you think)
  • setup a simple JavaScript framework to query the WordPress API from the frontend

As that procedure eats up a whole lot of project hours, the time was ready for an easier solution. So, together with my colleague Oliwer at Northosts and much appreciated input and testing from my friend Samuel, I started working on a WordPress/Nuxt.js hybrid development environment. Our goal was to provide a one-command-installation, which makes you ready to go in 5 minutes.

Now we are finally ready for our first release, so say hello to WUXT!

WUXT Components

WUXT combines a couple of components you almost certainly already know from other projects, adds some convenient new functionality and solves a handfull of configuration problems. Following a short overview of all components

Docker containers: To provide a consistent development experience on all platforms, we use Docker (what else?). Three containers are needed, one for the WordPress database (mysql.wuxt), one for WordPress (wp.wuxt) and one for nuxt.js (front.wuxt). Everything is set up with docker-compose. We were extra careful to base everything on the official images (mysql, wordpress and node) and skip custom docker-files, which should make it a lot easier to upgrade the environment. The relevant files from the container are mirrored to the host with volumes, which makes the database persistent (_db folder), lets you access everything important in WordPress (wp-content directory) and nuxt.js (nuxt folder).

WordPress Rest API extensions: The WordPress API is awesome, but to use WordPress full strength in our nuxt.js frontend, we needed some additional functionality. Activating our WUXT-theme adds endpoints for menus, the front-page settings, getting pages and posts by slug and includes meta-data from well known plugins. We are constantly adding new endpoints to support even more use-cases.

WordPress connector in nuxt.js: It’s not difficult to setup some requests to the WordPress API from nuxt.js, but we wanted a standardized way. That’s why we included the wpapi node module into our nuxt application (you have always access via the $wp object). Even here we did some adjustments, to make sure the connection to the WordPress Rest API can be established both from the nuxt-container to the wp-container and from the client application to the wp-container. Of course, we also added some shortcuts to support our new endpoints for menus, front-page, custom post types etc.

Development tools: Docker is so great, but sometimes it makes developer lives a little bit more difficult. Logging in to the container to run a wp-cli command can be a hassle, file permissions when working with volumes might be another. To make that a little bit more easy we added gulp tasks to give you access to wp-cli or installing new node-modules inside the container, without accessing it. We even provide a post-type generator for gulp, which lets you create automatically loaded, API-ready post-types in seconds.

Nothing of the above is rocket-science, but I think the combination and packaging into one, complete environment should make a difference for everyone developing with WordPress and nuxt.js. Please try it out and give us feedback, issues or some of your spare-time to make WUXT better.

Getting started

First clone the repository to a directory you want, then change to that directory and simply start your containers (you need to have a running Docker installation of course):

docker-compose up -d

That starts the following containers:

  • MySql (mysql.wuxt) 
  • WordPress (wp.wuxt)
  • nuxt.js (front.wuxt)

Your containers are available at

  • Frontend: http://localhost:3000
  • Backend: http://localhost:3080http://localhost:3080/wp-admin
  • Database: docker exec -ti mysql.wuxt bash

Setup WordPress

After starting the containers the first time you have to do a common WordPress installation at http://localhost:3080/install.php. Then log in to wp-admin and select the wuxt theme to activate all the API extensions. Additionally you might want to activate the ACF plugin to make your meta work easier. Last but not least you have to set the permalink structure to “Post Name” in the WordPress settings.

To check if everything is running, visit http://localhost:3080 and verify that the WUXT info screen is showing.

Then check that the Rest API at http://localhost:3080/wp-json is returning a JSON-object and you are good to go.

Setup nuxt.js

Nuxt should have been started automatically inside the docker container. The command we use for running the nuxt.js server is yarn dev. Check if the frontend is running by opening http://localhost:3000. You should be greeted by the Wuxt intro-screen.

Finally check if BrowserSync is running, by doing a minor change to the front-page. The change should directly be visible on the front-page as well.

Feedback

Please let us know what you think about WUXT, what you did with it and what you couldn’t do. Please clone, fork, open issues, comment or contribute, it’s much appreciated.

I am a great fan of Elastic Search, the ElasticPress plugin and the ElasticPress WooCommerce module. The other day, when I was working on a clients WooCommerce shop, it was the first time I wasn’t satisfied with the quality of the search results, though.

After some digging with the Debug bar and its ElasticPress extension, I could narrow down the problem to a combination of many resembling titles in our database and the Elastic Search fuzziness parameter. Of course the fantastic folks at 10up provide a filter for that in their ElasticPress plugin.

 

So here is how to disable fuzziness in the search:

function themeslug_deactivate_ep_fuzziness( $fuzz ) {
    return 0;
}
add_filter( 'ep_fuzziness_arg', 'themeslug_deactivate_ep_fuzziness' );

Of course you can even raise the fuzziness with the same method. If you want to adjust fuzziness dependent on other search parameters, the filter provides two more arguments, $search_fields and $args, which might help.

Recommendation: If you want to use WordPress with ElasticSearch, you can get both with Kinsta. Read more on how to speed up WordPress with their ElasticSearch solution on the Kinsta blogg.