Current WordPress Rest API extension list
- Front-page endpoint
- Menu endpoint
- Taxonomy filter attribute
- Integrating ACF meta fields
- Integrating Yoast meta fields
- Slug endpoint for posts and pages
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; } ?>
Menus
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!