WordPress REST API Content Endpoints

Yesterday, Helen Hou-Sandí—WordPress lead developer, and release lead for the current WordPress 4.7 release cycle—asked on Twitter for things that people wanted to build using WordPress REST API endpoints but didn’t because they aren’t built into core.

What she then even more is interested in, can be seen in the follow-up questions she tweeted in reply to various received answers: if/how the current status of WordPress Core is blocking these plans, and whether or not the things included in the current REST API merge proposal really can help building these individual things.

Working as a WordPress engineer at Inpsyde, I was and still am involved in several projects that make use of the WordPress REST API. So I responded to Helen’s question with the first thing that came to my mind. Here’s the Twitter chat:

I happily comply with Helen’s request, but as the combination Twitter and I obviously is far from ideal to do so, I rather write up a short post. Well, this is it. 🙂

WordPress REST API Merge Proposal

The current merge proposal lists two parts for Core inclusion—Content and Authorization—which will be briefly discussed in the subsequent sections.

If you are interested in more details and/or want to leave feedback, please also have a look at the dedicated Merge Proposal Discussion post, published also just yesterday.

Content API Endpoints

The first, and greater, part of the merge proposal is all about API endpoints providing read and write access to the following content types:

  • Posts
  • Comments
  • Terms
  • Users
  • Metadata (for any of the four previous types)

These endpoints will allow you to fully manage the above content, meaning: CRUD functionality. One could, for example, create and publish a brand new post, approve a currently pending comment, fetch all tags of a specific post, update the bio of some author, or delete some custom field added by plugin Whatsitsname.

Authentication

Besides the content endpoints, the merge proposal also includes authentication via OAuth 1. This would allow the REST API to be used from remote servers and apps, in a standardized and secure way. The currently cookie-based authentication is suited for internal usage of the REST API only.

The interesting thing about this is that the authentication would not be merged off the WordPress REST API plugin. The proposed code is currently available in the separate WordPress REST API OAuth 1 Server plugin. Unfortunately, this plugin raised several concerns.

Communication in a WordPress Network

Okay, now that we’re up to speed, back to my something.

Being the lead developer of MultilingualPress—a multisite-based plugin for multilingual websites—I maybe (also) think about using the REST API in a different way than most people. While the common use case is having some external app/client/consumer that seeks access to some WordPress website’s data, a rather important task for multisite-based plugins is accessing some specific data on some specific site, from another site in the network.

By using switch_to_blog(), one can easily switch the site context. And oftentimes, this is enough already. Sometimes it is not, though. Things get rather cumbersome when you have to access something off another site, and you need that something in the exact same state that you would get it in if you actually logged in to the specific site. For example, switching sites does not also switch the text domain (i.e., the translations), or re-register custom post types, or regenerate rewrite rules. Also, switching to a site will not suddenly activate a plugin that would be active on the site, but is not on the site you actually logged in to.

A Concrete Usage Example

Using MultilingualPress, you have a WordPress multisite installation where, in the default setup, each site represents a specific language version of your website. When a visitor sees a specific post in, say, English, they can easily switch to any of the available translations (i.e., a post on another site in the multisite).

The relations between all posts that form a translation group are stored in a custom table. This means, for every post in every site, MultilingualPress knows whether or not there are any translation posts available, and if so, what the individual post IDs are.

The problem is that MultilingualPress cannot just provide pretty permalinks for the individual remote posts. The reason is that there could be a fragment inside the according permalink (structure) that was run through gettext(), meaning it got translated into the current language. This could be the slug of a custom post type, for example. From the English site, you would generate http://example.com/de/product/maus, where the correct permalink would be http://example.com/de/produkt/maus (produkt with a k).

It might be possible to hack a site into the desired state (and reverse all of this afterwards), but this is really not what you want to (have to) do. Using the REST API to just ask a site for some data is.

Ease of Use

One is interested in some data of some site. Instead of hacking the context as one thinks is right, one rather would like to just get the data served, no extra work necessary.

As long as one has all the information required for the according endpoint (that has to exist and provide what one needs, of course), one simply has to request some specific URL, wait a couple seconds, and just do with the received data whatever one likes.

Efficiency

Not only is using the REST API by far easier than doing any extra work, it is also more efficient. You know what you want and where to find it. You only lack the means to (just) get it.

Indeed, one could also do this the admin-ajax way. But this would mean one has to rebuild lots of the infrastructure that the REST API already has.

Would the Proposed Merge Help?

Absolutely!

To be honest, I don’t really like the architecture of the WordPress REST API. That’s why I built WP REST Starter—a Composer package providing proper object-oriented equivalents of all the general-purpose things you can find in the WP_REST_Controller class of the WordPress REST API plugin. But that’s not what this post is about.

The functionality, on the other hand, meaning all the endpoints to use the REST API to access your website’s content, this is what brings so many things we all have been waiting for (or were forced to build our own stuff for this).

What Do You Think?

Maybe I just waited for too long, and now I’d take all I can get…?

Do you also want the Content API and/or OAuth 1 authentication get merged into Core?

Is there something you think is wrong, missing, or too much? I’m really interested in feedback.

2 Comments

  1. Helen H-S

    Some follow-up questions (apologies that some of these are probably a bit involved): What would be your plan for navigating the multisite part? What might that code look like for you? Is how multisite is handled (or not handled) satisfactory to you? Even if you feel like disagreeing with it architecturally may be futile, what kind of a problem does that represent to you?

    1. Hi Helen,

      I’m not sure I understand all of your questions, but I’m trying. 😉

      What would be your plan for navigating the multisite part?

      If you are talking about how MultilingualPress would be able to get the desired information off a specific site, from another site, this would be something like this:

      for every single post, MultilingualPress can easily look up related content from a custom table;
      this means that you have (site ID, content ID) combinations;
      get the according (/wp-json/) URL for a given site ID;
      query the REST API;
      display the permalink (in my concrete example here).

      Essentially, there wouldn’t be any navigating the multisite included at all.

      What might that code look like for you?

      Let’s assume I already handled step 2 from above (looking up the related posts for a specific post in the current site). This means I now have an array with site IDs as keys and the according post ID as values.

      Getting the according URL is easy to do by using get_rest_url( $site_id, $optional_path_here ). After this, I am able to query the Posts endpoint of the remote site to retrieve the response object for the post with the ID in the array.

      Going further is nothing special: check the response, json_decode() if OK, and just use the permalink to build my navigation.

      But maybe I didn’t really understand your point here…?

      Is how multisite is handled (or not handled) satisfactory to you?

      You mean in the REST API context? I’m unaware of anything multisite-related or -specific. What did I miss?

      There is no Sites endpoint yet, that’s why for the MultilingualPress REST API part—still in development, nothing public yet—I created a Sites endpoint tailored to the exact needs. The better thing to do here is to add further fields (via register_rest_field()) to the Sites endpoint shipped in Core.

      Even if you feel like disagreeing with it architecturally may be futile, what kind of a problem does that represent to you?

      It doesn’t really represent a problem. It’s just that using a handful of functions and maybe writing up a custom class that extends a gigantic abstract controller class provided by the WordPress REST API plugin essiantially results in procedural code, with the used functions not even being pure (as they are using global variables). I don’ like procedural code (for several reasons) too much. So I built WP REST Starter, for working with the WordPress REST API in an object-oriented fashion.

      Using the interfaces and default implementations provided by WP REST Starter helps me write proper object-oriented code that is easier to understand, easier to extend, easier to test, and easier to maintain. Furthermore, in case the internals of the WordPress REST API will ever change, I’ll have WP REST Starter take care of this, if possible. So this is also some abstraction layer without the need to adapt all my RESTful WordPress projects.

      I hope this is (part of) what you wanted…? 🙂

      Thanks,
      Thorsten

Leave a Reply

Your email address will not be published. Required fields are marked *