Making WordPress REST API Calls Internally

WordPress REST API

The WordPress REST API is great for gaining access to your data from external sites or from front-end JavaScript applications. It is even great for one-off calls you probably used to make via normal WordPress AJAX. However, have you ever wanted or needed to make WordPress REST API calls internally?

What do I mean? I mean being able to make a WordPress REST API call directly within PHP without making an HTTP request. Of course, this would only work if you want the data out of the site on which your code is running.

Why?

You are probably thinking, isn’t this what WP_Query is for? Why use an internal REST API call to do something that WordPress already allows you to do? Well, the main reason is because WP_Query returns data in a completely different format than the REST API.

My particular use case for needing this was that I wanted to avoid making an initial HTTP request to fetch the initial data for a JavaScript application. While I could have the JavaScript application make an HTTP request as soon as it loaded, there were a few reasons why an internal REST API call would make more sense:

    1. I could avoid the HTTP request altogether by making an internal query and passing the initial data to the app directly from PHP. This would make my application load much faster.
    2. I could avoid the lag where a user sees the initial state of the application with no data until the initial HTTP request completes. This would make my application not only be faster but also provide a better user experience. There would be no need for initial loading graphics, time to interaction would be less, etc.
    3. In the event that users don’t actually interact with the application, no additional HTTP requests are made to the web server. If we made the extra HTTP request, we would end up loading WordPress and all the plugins twice as opposed to just once. Depending on our application’s bounce rate, this could significantly affect the load on the web server.

How to Make an Internal WordPress REST API Call

It isn’t difficult to make an internal REST API call, but it is a bit more involved than your normal WP_Query usage. Also, keep in mind that the parameters you pass to the REST API are not always the same as what you would pass to WP_Query. For example, the post_status query var would become status when making an API request.

<?php

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request->set_query_params( [ 'per_page' => 12 ] );
$response = rest_do_request( $request );
$server = rest_get_server();
$data = $server->response_to_data( $response, false );
$json = wp_json_encode( $data );

Basically, you will start by creating a new instance of a WP_REST_Request. This takes two parameters: the method and the route. Since you will most likely only be querying data, you’ll probably only use the GET method for internal requests although you could make POST requests as well.

Once you have a request, you can call the set_query_params() method to pass any arguments that you would normally have set in the URL query string. You can set headers as well, but it is important to note that authentication isn’t performed when making internal requests. As such, you will want to make sure that normally protected endpoints aren’t exposed by using this method. Be sure to add any applicable capability checks.

Once you’ve set up your request object, just pass it to the rest_do_request() function to get a response object.

Finally, get an instance of the REST server by calling rest_get_server(). Then call the response_to_data() method on the server and pass it your response object. The second parameter is the embed option. If false, things like authors and featured images won’t be embedded in the data. If true, they will be embedded. What is returned by doing this is an array containing the data you would normally get in JSON format from the REST API. All you need to do to get actual JSON is to run the data returned through the wp_json_encode() function.

So what do you think? Have you done anything like this before? If so, I’d love to hear more about your use case!

Comments

  1. Thanks, this helped a me a lot! We use our API with JavaScript for the most part, but a scenario came up where it was preferable to consume the data with PHP.

  2. Thank you much much! Been searching for something like this for a while now.

    I’ve been creating serializer/transformer classes that transform each post type into something that matches the API’s JSON structure, but haven’t felt good about it, especially since I use node-wpapi for requests after the initial page load. I’d attempted to use the REST controllers’ prepare_item_for_response function and a few other shots, but to no avail. Things like acf-to-rest-api weren’t called.

    I’m using this for the same reasons as you: __INITIAL_DATA__ to avoid the extra HTTP request (for a Vue application in my case). And for the consistent object structure.

    Just gave this code a quick spin, and it’s working perfectly. The acf-to-rest-api and better featured image hooks work as they would from an actual REST request.

    The only complaint I have about an internal request is that I’m still limited to per_page <= 100 per request… sure would be nice if internally I could up that one, but it's still easy enough to handle.

  3. Thank you, Micah.

    I am planning to do something very similar and your article helped greatly. The same “why would I do this?” thoughts came to my mind and I am glad I am not alone 🙂

    Anton

  4. Well done post, Micah. A nice solution for a legitimate purpose. I’m creating several Vue apps on various pages of my WP site and initializing them with data using this method speeds up initial page loads without forcing me to deal with data formatted in two different ways.

  5. Thanks. I used it because the author of the Redirect plugin said the internal API might change but the REST API should be stable.

    FYI, the code above doesn’t work inside wp shell, but it did work perfectly in PHP.

  6. Very cool. Thanks for that, it helped simplify something I’m writing considerably.

    I did run into authentication issues with using the WooCommerce API and this method though. Tried pulling up a custom endpoint I’m writing that’s GETting from the Orders API and kept getting a 500 error in my logs. Dug a little deeper and found out it couldn’t read the posts. (From a browser with no credentials, of course it can’t. Duh, self. haha) Pulled up that custom endpoint in Postman with valid credentials, and everything works fine.

    It seems like you don’t necessarily need to REauthenticate, but it will use your current credentials to determine what you can access.

    Hope that helps someone else who comes across this at some point. 🙂

  7. Took me AGES to find an answer to this so thanks. Asking in forums all I got was – why do you need to do this? You need to change your design.

    Actually, you’ve got as simple use case as – I’m making a custom endpoint to update a post with a custom process and I want to return the REST object like every other REST POST request does…

  8. Wow! Amazing! I was looking for a sample of this ready made – didn’t feel like sifting through the WP REST API code.

    We’re building a website which has a basic plan with fixed capped features and no API access, and a few other plans include API access and have usage based pricing. And this allows us to build everything on top of the API, the capped plan will use the API internally, and the others will use it externally.

    Thank you!

  9. Thanks for this.
    Thanks for this.

    I have it working, but is there any way to make it non-blocking?

    In part of my set_query_params() array, I’m using:
    ‘timeout’ => 0.01,
    ‘blocking’ => false,
    ‘sslverify’ => false,

    But it’s still finishing before moving on to the next task.

    1. It isn’t possible to use those parameters here since this isn’t actually making an HTTP request. All of those params only work with the wp_remote* functions. The $request->set_query_params() only allows you to use arguments that you’d normally pass in the URL when making an external REST API request.

  10. Very helpful thank you!

    I turned it into a little function.

    function local_wp_api_call($route, $query_params = [], $method = ‘GET’ ){
    $request = new WP_REST_Request( $method, $route );
    if($query_params){
    $request->set_query_params( $query_params );
    }

    $response = rest_do_request( $request );
    $server = rest_get_server();
    $data = $server->response_to_data( $response, false );

    return $data;
    }

  11. When you say “authentication isn’t performed when making internal requests”, I’m finding on my server, this isn’t the case.

    I can only perform these requests if I am logged in. Otherwise the request fails and says “rest_forbidden” “Sorry, you are not allowed to do that.” in the response — regardless of the request being made internally locally.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.