In this post I’ll attempt to create an overview of how to create custom REST API endpoints and perform requests for them in a custom Gutenberg block. That is, making requests with fetch methods for information not available in WordPress’ registered data stores.

A friendly reminder: most basic information are already available in WordPress’ data stores. For example basic querying of posts, pages, custom post types, taxonomies, authors, media, and more are available as-is without having to create your own custom endpoints. In order to access these stores you’d rather use WordPress core data module (withSelect and select()). Below is a tutorial part that goes in depth in how to do that.

WordPress REST API

If you didn’t know already; WordPress REST API is a JSON interface to send and receive data from your WordPress site. It can be used externally or internally. With the Gutenberg editor and the switch to Javascript, the uses for REST API has definitely increased. WordPress REST API has a whole bunch of endpoints that we can use. See a complete reference on all REST API endpoints here. You’ll find for example endpoints for posts and most other internal content – both for reading and updating. Theme or plugin developers can register their own custom endpoints.

Your WordPress site has a root REST API URL, as default located at <your domain>/wp-json. For instance a local WordPress with the URL http://localhost/wordpress/ can access the REST API at http://localhost/wordpress/wp-json. From there we need to append endpoints. Referring to the above reference of endpoints we can retrieve a list of latest posts in the endpoint /wp/v2/posts. That means if you go to http://localhost/wordpress/wp-json/wp/v2/posts in your browser you will get a JSON formatted response of the latest posts in your WordPress site.

A note on namespaces is in order. A REST API URL starts with a namespace (‘wp/v2‘ is WordPress’ namespace as seen in the example URLs above). Namespaces is a concept to avoid clashes in core WordPress, themes, and plugins adding endpoints with the same name. Create your own unique namespace – typically a slug form of your theme or plugin name. After the slug a general rule is adding the version number, normally starting at v1. As an example my theme’s slug is ‘awhitepixel‘, so if I were to create custom endpoints in my theme I’d use the namespace ‘awhitepixel/v1‘. With this namespace I could register an endpoint ‘posts‘ and it would cause no problems even though it’s identical to WordPress’ endpoint name.

Working with REST API in WordPress is a large topic with a lot of good information available. In this post I’m focusing on usability in Gutenberg editor and how to retrieve them in Javascript.

What we’ll make, and what we need

The usability for requesting custom information has a wide array of use cases, so you’d usually need to customize the code examples below to fit your needs. The data could be a customized post query, a custom SQL query or something completely different.

When we create a custom endpoint, we are in full control of its return. We can perform any kind of operations and queries in WordPress/PHP and pass this onwards as JSON. And in our Gutenberg block we’ll be able to retrieve this return and do what we want with it within the block’s edit function. Typically you’d use the data to present the end-user with a choice or information within the block editor, but you can also store information from it in your block for further use. You can also create your own custom stores for this data, but I won’t go into how to do that.

I assume you are already familiar with how to create custom Gutenberg blocks so I won’t go through this in detail here.

Creating a REST API endpoint

Registering a custom REST API endpoint is done in PHP. You’d add this code in your theme’s functions.php or an active plugin code. Hook a function to the action rest_api_init, and run the function register_rest_route() for each endpoint you’d like to register.

Provide your namespace as first parameter, your endpoint route as second, and an array of settings as third parameter to register_rest_route(). The fourth parameter controls whether or not you want to override an existing route; not something we’ll look at here. In the array for the third parameter you should as minimum set the property ‘callback‘ to a function that is responsible for returning the endpoint’s data. Setting ‘method‘ is common as well, e.g. setting your endpoint to ‘GET‘, ‘POST‘, ‘PUT‘, etc.

Let’s start with registering a simple endpoint;

php

add_action('rest_api_init', function() {
	register_rest_route('awhitepixel/v1', '/mydata', [
		'method' => 'GET',
		'callback' => 'awhitepixel_rest_route_mydata'
	]);
});

My theme’s namespace is ‘awhitepixel/v1‘ and I’m registering an endpoint ‘mydata‘ within this namespace. This means I can access my custom REST API at http://localhost/wordpress/wp-json/awhitepixel/v1/mydata.

When registering (or changing) REST API routes, you will need to flush your permalinks in order for it to work. You can do this by visiting Settings > Permalinks and simply click Save.

The above code does not work yet, because I have not defined the function set as callback: awhitepixel_rest_route_mydata. The callback function receives one parameter; an array of data with information and arguments passed in from the request. Finally you need to carefully consider your callback function’s return.

Firstly you must always return something from your endpoint callback. Any return will automatically be converted into JSON by WordPress. This means you can return practically any form of data in your function; a string, null, an array, or WP_Error instance. You can also opt for returning a WP_REST_Response object for more control on e.g. status code or header information. I recommend wrapping the return in the function rest_ensure_response() to ensure your response is a valid REST response.

Let’s define our callback function and return a simple string as a start;

php

function awhitepixel_rest_route_mydata($data) {
	$response = 'Hello there!';
	return rest_ensure_response($response);
}

With the above code (and flushed permalinks), I can now go to the URL http://localhost/wordpress/wp-json/awhitepixel/v1/mydata.

From here on we can add any kind of code in our callback function to generate proper data to return. You can query WordPress content with e.g. WP_Query, make queries in the database, or request external data. This part is up to you.

Now, let’s move on to the opposite side; how to make the requests.

Making REST API requests in Javascript

Performing REST request is commonly done using fetch in Javascript. WordPress provides its own wrapper around fetch that simplifies WordPress REST API requests; wp.apiFetch. This is what I’ll use in my custom Gutenberg block. Keep in mind that a fetch requests return a “promise” – so we need to chain a .then() in order to handle the actual request return. The base usage is something like this;

js

wp.apiFetch({
	path: '<namespace and endpoint>',
}).then(data => {
	console.log('response from apifetch: ', data);
});

apiFetch allows us to provide a path property instead of building the complete URL. All we need to provide is the namespace and endpoint, and apiFetch will append this to WordPress’ REST API root URL. Inside the .then() function we have access to the data which is already converted to JSON. It’s here you’d do something with the data. Usually you’d store the returned data in e.g. the component’s state.

Below is an example of a custom Gutenberg block’s edit component. It’s class-based in order to use state to store the returned data from the REST API request. This also allows us to run the request in componentDidMount() when it first mounts (see React’s documentation on lifecycle methods). All of this provides a simple example in order for you to understand the basic concept; not as a recipe to do it exactly like this. You might consider using React hooks and functional components or construct a higher-order component instead.

const { Component } = wp.element;
const { Spinner } = wp.components;

class BlockEdit extends Component {
	constructor(props) {
		super(props);
		this.state = {
			list: [],
			loading: true
		}
	}

	componentDidMount() {
		this.runApiFetch();
	}

	runApiFetch() {
		wp.apiFetch({
			path: 'awhitepixel/v1/mydata',
		}).then(data => {
			this.setState({
				list: data,
				loading: false
			});
		});
	}

	render() {
		return(
			<div>
				{this.state.loading ? (
					<Spinner />
				) : (
					<p>Data is ready!</p>
				)}
			</div>
		);

	}
}
export default BlockEdit;

The example above is a class-based componed that is supplied to the block’s edit function in registerBlockType(). It sets up a state object of an array to hold the data (this depends on the data you return, obviously), and a status boolean to know when the async request has returned. Once the component is mounted (rendered for the first time) it runs the function to perform the apiFetch request. We set the path to the endpoint we registered in PHP above. The method is by default GET so we don’t need to specify this in apiFetch. And inside .then() function when the request is ready we update the component’s state with the returned data.

Obviously your block’s render function would do more with the returned data itself. You might want to provide the data to the user somehow presenting a list to perhaps choose from. It all depends on what kind of data it is and what you want to use it for.

Passing arguments to the endpoint

In some cases we need to pass some arguments to the endpoint. Common uses are passing an ID after the endpoint; for example http://localhost/wordpress/wp-json/wp/v2/posts/14 would return the post ID 14.

This is pretty simple and is done by adding a regex search pattern to the endpoint when registering it. It requires some knowledge of regexes to build complex patterns, but below is an example that matches a number and assigns it the name ‘id’. Naming the matches gives us means to access the variable in our callback function. Let me show you what I mean.

Let’s register a new endpoint route. We use the same endpoint as we did earlier (‘awhitepixel/v1/mydata‘) but for this route we add a regex match for a number at the end.

php

add_action('rest_api_init', function() {
	register_rest_route('awhitepixel/v1', '/mydata', [
		'method' => 'GET',
		'callback' => 'awhitepixel_rest_route_mydata'
	]);

	register_rest_route('awhitepixel/v1', '/mydata/(?P<id>[d]+)', [
		'method' => 'GET',
		'callback' => 'awhitepixel_rest_route_mydata'
	]);
});

The regex pattern (?P<id>[d]+) seems cryptic, but will be pretty clear with some basic understanding of regex. The [d]+ part matches any number (0-9) 1 or more times. The (?P<id> and ) parts are for matching a named group. The group name is in this case id, but you can name your group(s) whatever you want.

You can choose to direct this endpoint to a separate callback function, but I’ve chosen to use the same function to handle both /mydata and /mydata/<ID> requests. This means that I can in my callback function do:

php

function awhitepixel_rest_route_mydata($data) {
	if ($data['id']) {
		$response = 'Create return for ID: ' . $data['id'];
	} else {
		$response = 'Create general return (no ID provided)';
	}
	return rest_ensure_response($response);
}

Remember that the parameter to the callback function contains the returned arguments. Because I named my matched group ‘id‘, the matched value will be accessible in $data['id']. And finally because I use the same function to handle requests with and without ID, I can easily switch between the two different returns.

With this (and refreshed permalinks), I will get these responses for my custom endpoints:

Share:

Leave a Reply