WordPress REST API Custom Endpoint
The built in routes cover posts, pages and more, but often you need your own: send a contact request, return a special list, or run a calculation. With register_rest_route() you can add an endpoint in a few lines. Do it in a plugin.
A simple GET endpoint
<?php
/**
* Plugin Name: TC API Demo
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'rest_api_init', function () {
// GET /wp-json/tc/v1/hello?name=Riya
register_rest_route( 'tc/v1', '/hello', array(
'methods' => 'GET',
'callback' => 'tc_api_hello',
'permission_callback' => '__return_true', // public endpoint
'args' => array(
'name' => array(
'default' => 'friend',
'sanitize_callback' => 'sanitize_text_field',
),
),
) );
// GET /wp-json/tc/v1/square/5 (the number must be digits)
register_rest_route( 'tc/v1', '/square/(?P<n>\d+)', array(
'methods' => 'GET',
'callback' => 'tc_api_square',
'permission_callback' => '__return_true',
'args' => array(
'n' => array(
'validate_callback' => function ( $value ) {
return is_numeric( $value ) && $value <= 1000;
},
),
),
) );
// POST /wp-json/tc/v1/notes (only for users who can edit posts)
register_rest_route( 'tc/v1', '/notes', array(
'methods' => 'POST',
'callback' => 'tc_api_add_note',
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'args' => array(
'text' => array(
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
),
) );
} );
function tc_api_hello( WP_REST_Request $request ) {
return array( 'message' => 'Hello, ' . $request['name'] . '!' );
}
function tc_api_square( WP_REST_Request $request ) {
$n = (int) $request['n'];
return rest_ensure_response( array( 'number' => $n, 'square' => $n * $n ) );
}
function tc_api_add_note( WP_REST_Request $request ) {
$id = wp_insert_post( array(
'post_type' => 'post',
'post_title' => $request['text'],
'post_status' => 'draft',
) );
if ( is_wp_error( $id ) ) {
return new WP_Error( 'tc_save_failed', 'Could not save the note.', array( 'status' => 500 ) );
}
return new WP_REST_Response( array( 'id' => $id, 'text' => $request['text'] ), 201 );
}
The parts of an endpoint
| Part | Meaning |
|---|---|
Hook rest_api_init | The place where routes must be registered |
Namespace tc/v1 | Your unique name and version. Use a name for your plugin, and change the version for big changes |
| Route | The path. Use a regular expression for parts like (?P<n>\d+) |
| methods | GET, POST, PUT, DELETE (or constants like WP_REST_Server::READABLE) |
| callback | The function which makes the answer |
| permission_callback | Who can use it. It is required. Use __return_true for a public endpoint |
| args | Description of the parameters: default, required, type, validation and sanitizing |
Test the endpoints
$ wp plugin activate tc-api
Plugin 'tc-api' activated.
Success: Activated 1 of 1 plugins.
$ curl -s "http://example.com/wp-json/tc/v1/hello" | jq -c .
{"message":"Hello, friend!"}
$ curl -s "http://example.com/wp-json/tc/v1/hello?name=Riya" | jq -c .
{"message":"Hello, Riya!"}
$ curl -s "http://example.com/wp-json/tc/v1/square/12" | jq -c .
{"number":12,"square":144}
Errors from validation and routes
$ curl -s "http://example.com/wp-json/tc/v1/square/5000" | jq -c '{code, message, status: .data.status}'
{"code":"rest_invalid_param","message":"Invalid parameter(s): n","status":400}
$ curl -s "http://example.com/wp-json/tc/v1/square/abc" | jq -c '{code, status: .data.status}'
{"code":"rest_no_route","status":404}
The number 5000 is a valid route, but it failed our validate_callback, so WordPress answered 400 with the code rest_invalid_param. The text abc does not match the route at all, so there is no route (404).
Protected endpoint
$ curl -s -X POST "http://example.com/wp-json/tc/v1/notes" -d "text=Call the plumber" | jq -c '{code, status: .data.status}'
{"code":"rest_forbidden","status":401}
$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/tc/v1/notes" -d "text=Call the plumber" | jq -c .
{"id":5,"text":"Call the plumber"}
$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/tc/v1/notes" | jq -c '{code, message}'
{"code":"rest_missing_callback_param","message":"Missing parameter(s): text"}
Without a login the permission callback said no (401). With the application password, the note was saved and the code is 201. When the required parameter text is missing, WordPress gives a clear error by itself.
Return errors in the right way
- Return
new WP_Error( 'code', 'Message', array( 'status' => 404 ) )to send an error with the HTTP status. - Return an array or
WP_REST_Responsefor a success. - Use
rest_ensure_response()to wrap a value into a response object. - Do not
echoordie()inside the callback. Return the data.
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |