One of my always-download podcasts for leveling up on WordPress is Apply Filters with @pippinsplugins and @BradT recently put out a call for development-centric questions. I offered up a question in a haphazard series of tweets relating to different approaches to extending / customizing the WordPress Rest API:

After a few years iterating as a feature plugin, two short months ago WordPress 4.7 brought Rest API infrastructure for default content types: posts, pages, custom post types, taxonomy, users, etc into core. Each now has their own route under /wp-json/wp/v2/ with endpoints for GET, PUT, POST, PATCH, DELETE including securing updates and deletes to extensible form(s) of  authentication. The documentation available in the developer handbook is exceptional, but it is still early days so there is LOTS of room right now for well-written tutorials and conversations.

I hoped my question suggestion would spur Brad and Pippin to talk about the different ways to leverage the Rest API infrastructure for custom post types and managing meta data. My experience with it (during feature plugin days) was focused on integration with existing WordPress-as-CMS patterns like read-only calls to the API through jS for dynamic data presentation within a theme or populating drop-down lists for ACF driven forms. As I begin prototyping projects with Backbone or other Javascript SPA development models for front-end code, I know more business logic has to be baked into the back-end of the API and see a number of different approaches which the core API infrastructure provide.

Context Use Case – Basketball Tournament Tracker

While it isn’t the project that was in mind when I tweeted, the following is a quick summary of equivalent functional details mapped onto a much easier example / metaphor of tracking stats for a basketball tournament:

  • You want to track points and assists per position regardless of player.
  • Volunteers are filling in the stats that should be role-limited to specific games / teams
  • Tournament staff should be able to edit scoring totals
  • Goal is to track data in close to real-time
  • Front-end will be built with theme / rewrites serving a page for JS Backbone client to operate
  • Each game is setup as a custom post type with 10 meta data fields for scores
    (center points, center assists, shooting guard points, shooting guard assists, etc)
  • There will also be other meta data on the CPT which volunteers should not be able to edit.

As I explored the API source code, tutorials and developer handbook, I think I can describe 3 notably different approaches which one could consider using (YMMV). Before breaking them down, a review of the business logic, permissions checks, validation and update processes which might need to exist for an example project like this in order to stick to the API best practice validate everything:

Data should always be assumed to be bad until it’s been through some kind of validation process. Make no assumptions about the data you’re receiving — someone, somewhere will likely send you a request that will break something at some point. Treat it like you would a UI — always validate your data

  1. No user should be able to delete games via the Rest API
  2. All game scoring data must be an integer and less than 150 (No LBJ’s in this league folks)
  3. Volunteers should only be able to edit scoring meta fields.
  4. Volunteers should only be able to edit scoring meta fields for games they are assigned.
  5. Once a game has been completed, volunteers should not be able to modify game meta fields.
  6. Tournament staff should be able to edit any game scoring data at any point.
Scenario 1 - Extending Core By Registered Meta Fields Only

The core REST infrastructure is impressive for how much functionality you get for so little net new code. Setting show_in_rest on the register_post_type for “game” would build out the route /wp-json/wp/v2/game/. Registering the rest fields with an update callback including business validation to confirm format, role access and number range before passing to update_post_meta could address points 2 thru 6 above. This gist outlines an approach for this scenario.

  • PRO – Relatively small amount of code unique to the business logic per field type
  • CON – A lot more data is being sent across the wire than is truly needed for use case
  • CON – Core permissions check happens at the CPT level. A requirement to check user role at field level happens per meta field.
  • CON – Core runs update_post before meta properties or update_additional_fields_for_object.
    A failure returned on the meta level may still completed the post update.
Scenario 2 - Completely Custom Route / Endpoints

If the well-structured model of core CPT routes is too restrictive for your use case, you could skip the show_in_rest flag and still use the same infrastructure provided by register_rest_route to allow you to roll your own.

  • PRO – get / update callbacks can be as granular as you need
  • PRO – Only as much data as you want to send across the wire will go
  • PRO – permissions callback can happen once for all data prior to any updates
  • CON – May need to reproduce a LOT of what core provides “for free” (GET – pagination, search, basic field validation)
  • CON – Future enhancements to core routes / endpoints are not applied to yours.

Note: If you were implementing a custom table and DB abstraction layer (hat tip again to Pippin) this would be the only real option.

Scenario 3 - Customizing Core Routes via Overrides

There is a middle path thanks to register_rest_route having an override parameter. One could use the existing GET endpoint for it’s ‘out of box’ pagination, search and structured goodness, while overriding the POST endpoint similar to what you would have built from scratch in scenario 2. I’ve read a few articles suggesting this may not be a highly recommended approach, but I believe that was in context of giving consistent responses to all front-end clients. If the project is one where you have control of both REST API and front-end, I would think this could be a very good option.

  • PRO – Maintains existing schema, request / response structures
  • PRO – Addresses permission / update timing concerns of Scenario 1
  • PRO – Only customizing the route / endpoints you need to
  • CON – Still sends more data across the wire than optimal

What did I miss? What would you choose?

It was highly unpossible to fit all of that into 140 chars, but hope that Brad / Pippin got the gist of question from tweets and will discuss on Apply Filters. As I mentioned at the outset – it is still early days for REST API in WordPress as well as my own personal usage of it.

  • Is there a much better pattern I didn’t cover that you would recommend for this use case?
  • What helps you chose between these options when starting up new projects?

If you’re interested in such questions / topics are you aware of Day of Rest Boston conference? I will be attending and from the workshop / conference write-ups feel like it is a great crash course in best rest practices.