‹ Back to all articles

Playing With Wordpress API

2025-04-16 23:25:07

I use WordPress a fair bit.

A lot of the time for work purposes. In fact - pretty much exclusively for work purposes now. More often than I'd like, it's page builder stuff. But maybe it's slightly ironic that as the page builders get bigger and 'better' I am finding I have to do more coding to get WordPress doing what we want.

I think this is probably because other people in my team are doing the page building, the making things look nice and making the clients happy. This means that stuff tends to reach me because Elementor or the plethora of plugins available just don't quite cut it. I am certainly not complaining about that, it means I get to code more!

Over the last few days I've had a couple of tasks that have resulted in the need to create custom API endpoints and tinker with them. It's been a lot of fun. One of the projects involved Site A needing data that is kept up to date on Site B, so a kind of headless approach, the other is a hybrid approach where I'm using API calls within the structure of WordPress. I've noticed a couple of things. They may be obvious to you.

Firstly - running stuff through the API makes for a much faster front end. We cache, we optimise, we prune, we give the website Lucozade, but still our WordPress sites are slow. Sure we can make them quicker, but in truth our natural tech stack isn't the fastest. Using the JS front end approach allows us to make stuff run much quicker.

Secondly - it seems so much easier to use modern dev tools! Even if it's just running git. But at the moment the endpoints are ready only, I can easily be using the 'live' database (ok, staging really, but same idea...) and running the front end on my local machine. It's much safer. And makes DB conflicts a thing of the past!

Finally - designing endpoints is fun.

Wow. That was quite an introduction. I was actually going to go through how I made a bit of an API today, but I'd be amazed if anyone has made it this far. In truth, what I'm going to say is covered, really clearly, in the WordPress Developer Resources but I've realised lately I need to get better at explaining things. So, if you're still with me, here we go.

Step 0
Just so we don't break anything, it's important to have a function ready for the endpoint. So I created an empty function first:

<?php 
/** 
 * Fetch the rooms 
 * 
 * Handles a REST API request and returns room data in JSON format.
 *
 * @param WP_REST_Request $request The REST request object
 * @return WP_REST_Response JSON response with room data or empty array
 */
 
function fetch_the_rooms($request) {
    return null;
}

A bit of a docblock to explain where the function is heading, and to help the IDE know it's on the right track, but just return a null now, so that nothing breaks when we call the function. I'll flesh this out in step 2.

Step 1

<?php
add_action('rest_api_init', function() { 
  register_rest_route('access/v1', 'rooms', array( 
    'methods' => 'GET', 
    'callback' => 'fetch_the_rooms', 
  )); 
}); 

I add an action to the inititian of the WordPress REST API sending it a function called register_rest_route which accepts the new namespace, the route it's pointing to, and the options that route will handle. As I mentioned before, at the moment this project is only reading the database, so GET is ideal, I add the function the route will call when people hit the endpoint using callback.

Note - I've not used any form of authentication at this stage, the truth is we want this stuff to be open to the public anyway, but I could add in permission_callback which lets you determine whether the callback should be run or not, you can use built in WordPress functions or roll your own to add authentication requirements.

So, for this example, that's pretty much it.

Step 2
In step 0 we created the function, it needs to exist. But until now the function doesn't do anything.

function fetch_the_rooms($request) {
    $args = [
        'post_type' => 'room',
        'posts_per_page' => -1,
    ];

    $query = new WP_Query($args);

    $rooms = [];
    foreach ($query->posts as $room) {
        $rooms[] = [
            'id' => $room->ID,
            'title' => get_the_title($room),
            'type' => get_field('room_type',$room),
            'availability' => get_field('availability',$room),
            'lowest_rate' => get_field('room_lowest_rate', $room),
        ];
    }

    return rest_ensure_response([
        'success' => true,
        'rooms' => $rooms
    ]);
}

This is mostly just nuts and bolts old school WordPress development. Creating a new query with some args (that can easily be extended and added to - I've called all the rooms as I know there will be a maximum of 12 in each call), and then looping through the rooms before turning that into a REST response.

The joy now is working out the conditions and filters that need to be in place. One key bit here is I only want the rooms linked to the property that is being viewed. Firstly, get the parameters doing something like this:

$parameters = $request->get_params();

And then add those into the loop creation.

$meta_query = [];

if (!empty($parameters['property_id'])) {
    $meta_query[] = [
        'key'     => 'room_property',
        'value'   => $parameters['property_id'],
    ];
}

$args = [
    'post_type'  => 'room',
    'posts_per_page' => -1,
];

if (!empty($meta_query)) {
    $args['meta_query'] = $meta_query;
}

Now when I hit the API at /wp-json/access/v1/rooms?property_id=45, I get a JSON list of all the rooms at property 45.

I didn't really do anything here that couldn't be done just using regular WordPress endpoints, but here I'm actually defining what data is retrieved. I'm in control of the data, and I can add in whatever I want, or in fact, take out what I don't need!

There's so much more to be done, this use case now has parameters to filter by property_id, availability, price ranges, type of room, and now they are there, I can do whatever I want with them... pretty much.