WordPress makes registering custom rest routes really easy with a function called register_rest_route
. This function provides options to create a namespace (which you can also use to version your APIs), all of your route names, an args array to create CRUD operations and check security, and even set your route to override or merge with existing routes.
Function definition
Let’s review the function parameters and see what we can do:
register_rest_route(
$namespace,
$route,
$args,
$override
);
- namespace : a required string which should be the same for all of your routes in the api you are creating
- route: the required string for the singular API endpoint, which would include some of the following methods: GET, POST, PUT, DELETE
- args: optional array of options or array of array of options (we’ll go over this in detail)
- override : optional true or false. True will replace an existing route while false will merge it and override any duplicate methods (defaults to false, so you can normally omit it)
Registering a route
To register a route, we need to call it inside of the rest_api_init
hook:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
//...we'll work in here
);
}
The first argument is the namespace. It’s important to note that your namespace should not include a beginning or trailing slash, and should be composed of your namespace and version number (for example, WordPress’s built-in routes use wp/v2
):
// we'll omit the action hook line from the rest of the examples:
function register_your_routes() {
register_rest_route(
'ndx/v1',
// route name
// args
);
}
Next, we will create our route name. This will be the last part of the URL we call to access this route. Because this is a URL, you should name it the way you would see a URL (dashes instead of underscores, etc) and avoid characters like , % [] { } # ( ) * &
and spaces:
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
// args
);
}
Finally, we add our args. Because of the scope of the args parameter, we’ll be going over the basic pieces in the next section
Adding methods, permissions, and a callback function
The most basic use of the args parameter is this:
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
array(
'methods' => 'GET',
'callback' => 'callback_function' // this can any function name,
)
);
}
This small amount of code will create a new route with a GET method that calls the callback_function
function. However, there are a couple of recommendations to make this better:
Permissions
With newer versions of WordPress, you are asked to set the permissions for your routes. It’s not required but you will get a warning message. To create a public route, just add this line to your args array:
array(
'methods' => 'GET',
'callback' => 'callback_function',
'permission_callback' => '__return_true' // new permissions line
);
Another way to set methods
Because there are multiple methods that seem very similar (POST vs PUT etc), WordPress provides some constants as part of the WP_REST_Server
class to make your method names more clear:
WP_REST_Server::READABLE // methods: GET
WP_REST_Server::EDITABLE // methods: POST, PUT, PATCH
WP_REST_Server::DELETABLE // methods: DELETE
You don’t need to instantiate the class so to use these just update the first line of your args statement:
array(
'methods' => WP_REST_Server::READABLE // was previously 'GET'
'callback' => 'callback_function',
'permission_callback' => '__return_true'
);
That’s all you need to declare a basic route (minus the callback function of course). Let’s see the code all together:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'callback_function',
'permission_callback' => '__return_true'
)
);
}
Creating your callback function
The callback function for your route is a normal PHP function, but it receives a full $request
object as it’s parameter:
function callback_function($request) {
// do stuff here
}
The $request
parameter is a WP_Rest_Request
instance and can contain body data, url parameters, and more. Now, let’s look at how we can return some data.
Returning data correctly
If you haven’t looked at our post on using rest_ensure_response
I would give it a glance here. You can skip to the end to see an example. The function returns your data with a 200 OK response header and any type of data you pass back (strings, arrays, etc). Here is a useless return value for our new route:
function callback_function($request) {
return rest_ensure_response('hello world!');
}
If you are working along with this article, you can test this yourself by adding the following to your base URL: /wp-json/ndx/v1/my-endpoint
Note: if you get a 404, it may be your permalinks. Go to Settings > Permalinks in the dashboard and turn on Pretty Permalinks. The specific style doesn’t matter, any of them will ensure /wp-json works correctly
If your route is setup correctly, you should see hello world!
in the browser.
Testing URL parameters
Now that we can return data, it would be nice to read data sent along with the API. If you’ve ever used URL parameters before, this should be straightforward. Change your callback function to this:
function callback_function($request) {
$name = $request->get_param('name');
return rest_ensure_response("hello {$name}!");
}
Theget_param
method is available from our WP_Rest_Response
instance, and can be used to read any URL parameters passed with the route. To test this, add the following to your base URL:
/wp-json/ndx/v1/my-endpoint?name=YOUR NAME
You should see “hello YOUR NAME!”
Basic error handling
If you remove the name parameter from the URL the result looks malformed. We can handle this by checking for the name param in our callback function and returning an error if it’s missing:
function callback_function($request) {
if(null !== $request->get_param('name')) {
$name = $request->get_param('name');
return rest_ensure_response("hello {$name}!");
} else {
return new WP_Error('missing_fields', 'please include name as a parameter');
}
}
Note that there is a better way to handle required input, which we’ll cover in our next article about data sanitization, but this is a completely valid way to check for a value. Also, do not try to use isset
with the get_param
method, as it already checks for this and returns null if it can’t find the parameter.
Adding additional methods to your route
Let’s go back to our route registration code:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'callback_function',
'permission_callback' => '__return_true'
)
);
}
If you wanted to add a POST method for your route, you might think of adding another register_rest_route
function declaration. That would required duplicating a lot of code with the same values. Instead, let’s change our args array to be an array of arrays:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'callback_function',
'permission_callback' => '__return_true'
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'another_callback_function',
'permission_callback' => '__return_true'
)
)
);
}
Notice how we now have two arrays that are very similar, but the second one’s method is EDITABLE and the callback function is different. This means you’ll need to create another function to handle this endpoint:
function another_callback_function($request) {
return rest_ensure_response('I show up when you call `my-endpoint` with the POST, PUT, or PATCH method');
}
If you add this code and refresh your browser you’ll notice you don’t see this message. That’s because accessing a WordPress API endpoint in the browser is always a GET request. To test other types of requests, you’ll need to use a library or something like Postman. We just posted a thorough tutorial on how to use Postman here.
Note: generally non-GET requests require authentication, but we are passing in our public permission callback, so you can skip that section of the Postman article and just get set up to play around with it.
Once you get set up in Postman, just make sure you change the method to POST, click the “Send” button and you should see this at the bottom:
Requiring values as part of the route
While URL parameters are a nice and flexible feature to send data to your route, you can also add data as part of the endpoint itself. For example, WordPress lets you access posts by going to wp-jons/wp/v2/posts
but it also lets you look at a single post by going to /wp-json/wp/v2/posts/<id>
. You can chain as many parameters as you want, although you will run into issues if some are required and some are not (what if the first parameter is optional, but the second isn’t?) It’s better to send multiple fields as URL parameters or body data.
If you do want to add a parameter as part of the endpoint, you do that to the $route
argument in your register_rest_route
function:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint/(?P<id>\d+)', // ADDED ID HERE
// ...args
);
}
The argument is wrapped in a regular expression, so let’s break it down:
'(?P<id>\\d+)' // the full argument (parathenses are to group it)
'?P' // denotes that this is a parameter
'<id>' // the name of the parameter
'\\d+' // indicates the paramter should be an integer
While this is hard to read, it does make sure the parameter is defined and has to be an integer. This change means the following calls are either valid or invalid:
/wp-json/ndx/v1/my-endpoint // not valid (missing the parameter)
/wp-json/ndx/v1/my-endpoint/10 // valid
/wp-json/ndx/v1/my-endpoint/hello // not valid (strings are not allowed)
To access the parameter in your callback function, just use get_param
like before:
$request->get_param('id');
What if you wanted to make the parameter completely optional, like how WordPress does for posts? Just make a new route! Here’s the full code with our routes we created before the example above, plus our new one:
add_action('rest_api_init', 'register_your_routes');
function register_your_routes() {
register_rest_route(
'ndx/v1',
'my-endpoint',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'callback_function',
'permission_callback' => '__return_true'
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'another_callback_function',
'permission_callback' => '__return_true'
)
)
);
// our new route
register_rest_route(
'ndx/v1',
'my-endpoint/(?P<id>\d+)',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'callback_function_with_id',
'permission_callback' => '__return_true'
)
)
);
}
Wrap Up
You are now ready to start creating your own routes! In our next article, we will go over the security features of register_rest_route
like custom permissions, sanitizing data, and validating passed-in parameters, so you can create routes for real-world use.
Author
Top comments (0)