1 of 37

The WordPress REST API: �A Development Primer

@ataylorme • Slides: goo.gl/pQcFZK

2 of 37

Hi, I’m Andrew

Agency and Community Engineer at Pantheon.

ataylorme on GitHub and Twitter.

andrew@pantheon.io

Some things I enjoy, aside from coding, are

  • Staying active - running, mountain biking, hiking
  • Craft beer
  • Soccer

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

3 of 37

In The Real World

@ataylorme • Slides: goo.gl/pQcFZK

4 of 37

WAMU.org

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

5 of 37

Of Hoops & Healing / Bostonia

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

6 of 37

Should I Use The REST API?

@ataylorme • Slides: goo.gl/pQcFZK

7 of 37

Opportunities

  1. Single page app experience
  2. Separation of concerns
  3. WordPress as a platform for other services
  4. Familiar backend for content creators
  5. Modern interface for interacting with WordPress

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

8 of 37

Challenges

  • Early days: Missing WordPress admin features
  • Early days: URL routing
  • Early days: WordPress architecture evolution
  • Authentication isn't turn key
  • Decoupled apps > WordPress theme

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

9 of 37

Routes and Endpoints

@ataylorme • Slides: goo.gl/pQcFZK

10 of 37

REST API Terms

  • REST API
    • Defines a set of functions which developers can perform requests and receive responses from over the web.
  • Namespace
    • Used to isolate and identify different functionality, e.g. core or plugins
  • Routes
    • A route is the “name” you use to access endpoints, used in the URL.
  • Endpoints
    • Endpoints are functions available through the API.
    • There can be multiple endpoints per route.

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

11 of 37

REST API Terms

  • HTTP Methods
    • GET should be used for retrieving data from the API.
    • POST should be used for creating new resources
    • PUT should be used for updating resources.
    • DELETE should be used for deleting resources.
  • HTTP Status Codes
    • Status is useful to the requestor.
    • For example, 400 (Bad Request) when an argument is passed in the wrong format.

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

12 of 37

REST API Terms

/wp-json/wp/v2/posts

Index

Namespace

Choose the endpoint by using different HTTP requests.�For example, GET to list posts and POST to create a new post.

Route

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

13 of 37

Default Routes

  • All routes are under the wp-json index
  • Default routes live under the wp/v2 namespace
    • e.g. the route for a single post is wp-json/wp/v2/posts/{ID}
  • Content endpoints in 4.7 include posts, terms, comments, users, meta and settings
    • Note: settings and meta are opt-in, meaning they aren't included by default. Core, plugins or themes need to register which items to include

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

14 of 37

Default Routes Example

Routes and endpoints are discoverable at /wp-json

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

15 of 37

Default Routes Example

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

16 of 37

Routes For Custom Post Types

$args = array('labels' => array('name' => _x( 'Books', 'post type general name' ),'singular_name' => _x( 'Book', 'post type singular name' ),),'description' => '','hierarchical' => false,'menu_position' => null,// No admin UI or archive pages'public' => false,// Expose through REST API'show_in_rest' => true,'rest_base' => 'books','capability_type' => 'post','supports' => array( 'title', 'editor', 'custom-fields' ));� register_post_type( 'books', $args );

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

17 of 37

Modifying Responses

register_rest_field('books','rating',� array('get_callback' => function ( $data ) {return (int) get_post_meta( $data['id'], '_book_rating', true );},'update_callback' => function ( $value, $post ) {� $value = intval( $value );if( $value >= 0 && $value <= 5){� update_post_meta( $post->ID, '_book_rating', $value );}},'schema' => array('description' => __( 'The rating, 0 - 5, for the book.' ),'type' => 'integer'),));

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

18 of 37

Modifying Responses

Modified response for /wp-json/wp/v2/books

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

19 of 37

Removing Items

function books_rest_prepare( $data, $post, $request ) {� $_data = $data->data;

� $unused_keys = array('date_gmt','modified_gmt','template',);�� foreach ( $unused_keys as $unused_key ) {if ( isset( $_data[ $unused_key ] ) ) {� unset( $_data[ $unused_key ] );}}�� $data->data = $_data;�� return $data;}��add_filter( 'rest_prepare_books', 'books_rest_prepare', 10, 3 );

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

20 of 37

Removing Items

Modified response for /wp-json/wp/v2/books

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

21 of 37

WP-Notebook Example

K. Adam White of Bocoup presented about the WordPress REST API during LoopConf. �If you missed it check out the recording.

He created an example app as a companion to the talk and it's a great example using a custom post type in the REST API with a React front-end. Check it out on GitHub.

K. Adam also released wpapi - an isomorphic JavaScript client for interacting with the WordPress REST API. If you are using JavaScript to parse the WordPress REST API I encourage you to check it out.

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

22 of 37

Custom Routes

@ataylorme • Slides: goo.gl/pQcFZK

23 of 37

Registering Routes

function register_books_rest_routes() {�� register_rest_route( 'app/v1', '/books, array('methods' => 'GET','callback' => 'list_books','args' => array(),) );

}��add_action( 'rest_api_init', 'register_books_rest_routes' );

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

24 of 37

Registering Routes

function register_book_details_rest_route() {

register_rest_route( ''app/v1', '/books/(?P<id>\d+)', array('methods' => 'GET','callback' => 'book_details','args' => array('id' => array('validate_callback' => function ( $param, $request, $key ) {return ( false !== get_post_status( (int) $param ) );}),),) );��}��add_action( 'rest_api_init', 'register_book_details_rest_route );

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

25 of 37

Registering Routes

'validate_callback' => function ( $param, $request, $key ) {if( ! is_numeric( $param ) ){return new WP_Error('books_bad_post_id',� __( 'Invalid post ID format. Please pass an integer.' ),� array( 'status' => 400 ));}� $post_id = (int) $param;if ( false === get_post_status( $post_id ) || 'books' !== get_post_type( $post_id ) ) {return new WP_Error('books_bad_post_id',� __( 'Invalid books post ID.' ),� array( 'status' => 400 ));}return true;}

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

26 of 37

Callback Methods

list_books callback method

book_details callback method

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

27 of 37

Custom Routes

Response for /wp-json/app/v1/books

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

28 of 37

Custom Routes

Response for /wp-json/app/v1/books/4

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

29 of 37

Testing

@ataylorme • Slides: goo.gl/pQcFZK

30 of 37

Why: Security

@ataylorme • Slides: goo.gl/pQcFZK

31 of 37

Why: Reliability

@ataylorme • Slides: goo.gl/pQcFZK

32 of 37

1. Use the PHPUnit test suite

Run wp scaffold plugin-tests or wp scaffold theme-tests to add the following files to your project:

  • .travis.yml OR circle.yml OR gitlab.yml
  • bin/install-wp-tests.sh
  • phpunit.xml.dist
  • tests/bootstrap.php
  • [and a few more]

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

33 of 37

2. Create Your First Test

Create a class Test_REST_API in tests/test-rest-api.php with:

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

34 of 37

3. Test Endpoints Thoroughly

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

35 of 37

Resources

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

36 of 37

Keep in touch!

Andrew Taylor

Agency and Community Engineer at Pantheon.

ataylorme on GitHub and Twitter.

andrew@pantheon.io

@ataylorme • Slides: goo.gl/pQcFZK

@ataylorme • Slides: goo.gl/pQcFZK

37 of 37

Questions?

@ataylorme • Slides: goo.gl/pQcFZK