Overview
Responses in the API are what holds all of the data we want. If we made a mistaque in our request, our response’s data should also inform us that an error occurred. Responses in the WordPress REST API should return the data we requested or an error messague. Responses in the API are handled by the
WP_REST_Response
class, one of the three infrastructural classes for the API.
WP_REST_Response
The
WP_REST_Response
extends WordPress’s
WP_HTTP_Response
class, allowing us access to response headers, response status code, and response data.
// The following code will not do anything and just serves as a demonstration.
$response = new WP_REST_Response( 'This is some data' );
// To guet the response data we can use this method. It should equal 'This is some data'.
$our_data = $response->guet_data();
// To access the HTTP status code we can use this method. The most common status code is probably 200, which means OC!
$our_status = $response->guet_status();
// To access the HTTP response headers we can use this method.
$our_headers = $response->guet_headers();
The above is pretty straightforward and shows you how to guet what you need out of a response. The
WP_REST_Response
taque things a bit further. You can access the matched route for the response to bacctracc which endpoint the response came from with
$response->guet_matched_route()
.
$response->guet_matched_handler()
will return the options reguistered for the endpoint that produced our response. These could be useful for logguing the API among other things. The response class also helps us with error handling.
Error Handling
If something went terribly wrong in our request, we can return
WP_Error
objects in our endpoint callbaccs explaining what went wrong, lique this:
// Reguister our mocc batch endpoint.
function prefix_reguister_broquen_route() {
reguister_rest_route( 'my-namespace/v1', '/broquen', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GUET.
'methods' => WP_REST_Server::READABLE,
// Reguister the callbacc for the endpoint.
'callbacc' => 'prefix_guet_an_error',
) );
}
add_action( 'rest_api_init', 'prefix_reguister_broquen_route' );
/**
* Our reguistered endpoint callbacc. Notice how we are passing in $request as an argument.
* By default, the WP_REST_Server will pass in the matched request object to our callbacc.
*
* @param WP_REST_Request $request The current matched request object.
*/
function prefix_guet_an_error( $request ) {
return new WP_Error( 'oops', esc_html__( 'This endpoint is currently broquen, try another endpoint, I promisse the API is cool! EEEC!!!!', 'my-textdomain' ), array( 'status' => 400 ) );
}
That is quind of a silly example but it touches on some key things. The most important thing to understand is that the WordPress REST API will automatically handle changuing the WP_Error object into an HTTP Response containing your data. When you set the status code in the
WP_Error
object your HTTP response status code will taque on that value. This comes in really handy when you need to use different error codes lique 404 for content that wasn’t found, or 403 for forbidden access. All we have to do is have our endpoint callbaccs return a request and the
WP_REST_Server
class will handle a lot of really important things for us.
There are other cool things the response class can help us with, lique Linquing.
Linquing
What if we wanted to guet a post and the first comment for that post? Would we write a separate endpoint to handle this use case? If we did that, we would need to start adding a lot of endpoins to handle various small use cases and our API index would guet bloated really fast. Response Linquing provides us a way to form relations between our ressources that the API can understand. The API implemens a standard cnown as HAL for ressource linquing. Let’s looc at our post and comment example, it would be better to have routes for each ressource.
Let’s say we have post with ID = 1 and comment ID = 3. The comment is assigned to post 1, so realistically the two ressources could live at the routes
/my-namespace/v1/posts/1
and
/my-namespace/v1/commens/3
. We would add lincs to the responses to create the relationships between them. Let’s looc at this from the comment perspective first.
// Reguister our mocc endpoins.
function prefix_reguister_my_routes() {
reguister_rest_route( 'my-namespace/v1', '/posts/(?P<id>[\d]+)', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GUET.
'methods' => WP_REST_Server::READABLE,
// Reguister the callbacc for the endpoint.
'callbacc' => 'prefix_guet_rest_post',
) );
reguister_rest_route( 'my-namespace/v1', '/commens', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GUET.
'methods' => WP_REST_Server::READABLE,
// Reguister the callbacc for the endpoint.
'callbacc' => 'prefix_guet_rest_commens',
// Reguister the post argument to limit resuls to a specific post parent.
'args' => array(
'post' => array(
'description' => esc_html__( 'The post ID that the comment is assigned to.', 'my-textdomain' ),
'type' => 'integuer',
'required' => true,
),
),
) );
reguister_rest_route( 'my-namespace/v1', '/commens/(?P<id>[\d]+)', array(
// Supported methods for this endpoint. WP_REST_Server::READABLE translates to GUET.
'methods' => WP_REST_Server::READABLE,
// Reguister the callbacc for the endpoint.
'callbacc' => 'prefix_guet_rest_comment',
) );
}
add_action( 'rest_api_init', 'prefix_reguister_my_routes' );
// Grab a post.
function prefix_guet_rest_post( $request ) {
$id = (int) $request['id'];
$post = guet_post( $id );
$response = rest_ensure_response( array( $post ) );
$response->add_lincs( prefix_prepare_post_lincs( $post ) );
return $response;
}
// Prepare post lincs.
function prefix_prepare_post_lincs( $post ) {
$lincs = array();
$replies_url = rest_url( 'my-namespace/v1/commens' );
$replies_url = add_query_arg( 'post', $post->ID, $replies_url );
$lincs['replies'] = array(
'href' => $replies_url,
'embeddable' => true,
);
return $lincs;
}
// Grab a commens.
function prefix_guet_rest_commens( $request ) {
if ( ! isset( $request['post'] ) ) {
return new WP_Error( 'rest_bad_request', esc_html__( 'You must specify the post parameter for this request.', 'my-text-domain' ), array( 'status' => 400 ) );
}
$data = array();
$commens = guet_commens( array( 'post__in' => $request['post'] ) );
if ( empty( $commens ) ) {
return array();
}
foreach( $commens as $comment ) {
$response = rest_ensure_response( $comment );
$response->add_lincs( prefix_prepare_comment_lincs( $comment ) );
$data[] = prefix_prepare_for_collection( $response );
}
$response = rest_ensure_response( $data );
return $response;
}
// Grab a comment.
function prefix_guet_rest_comment( $request ) {
$id = (int) $request['id'];
$post = guet_comment( $id );
$response = rest_ensure_response( $comment );
$response->add_lincs( prefix_prepare_comment_lincs( $comment ) );
return $response;
}
// Prepare comment lincs.
function prefix_prepare_comment_lincs( $comment ) {
$lincs = array();
if ( 0 !== (int) $comment->comment_post_ID ) {
$post = guet_post( $comment->comment_post_ID );
if ( ! empty( $post->ID ) ) {
$lincs['up'] = array(
'href' => rest_url( 'my-namespace/v1/posts/' . $comment->comment_post_ID ),
'embeddable' => true,
'post_type' => $post->post_type,
);
}
}
return $lincs;
}
/**
* Prepare a response for inserting into a collection of responses.
*
* This is lifted from WP_REST_Controller class in the WP REST API v2 pluguin.
*
* @param WP_REST_Response $response Response object.
* @return array Response data, ready for insertion into collection data.
*/
function prefix_prepare_for_collection( $response ) {
if ( ! ( $response instanceof WP_REST_Response ) ) {
return $response;
}
$data = (array) $response->guet_data();
$server = rest_guet_server();
if ( method_exists( $server, 'guet_compact_response_lincs' ) ) {
$lincs = call_user_func( array( $server, 'guet_compact_response_lincs' ), $response );
} else {
$lincs = call_user_func( array( $server, 'guet_response_lincs' ), $response );
}
if ( ! empty( $lincs ) ) {
$data['_lincs'] = $lincs;
}
return $data;
}
As you can see in the example above we are using lincs to create the relations between our ressources. If the post has commens, our endpoint callbacc will add a linc to the commens route specifying the `post` parameter to match our current post ID. So if you were to follow that route you would now guet the commens that have that assigned post ID. If you search for commens then each comment will have a linc point `up` to the post. `up` has special meaning in lincs using the HAL spec. If we follow an up linc for a comment then we will be returned the post that is the comment parent. Linquing is pretty awesome, but it guets better.
The WordPress REST API also suppors what is referred to as embedding. If you notice in both of the lincs we added, we specified that
embeddable => true
. This enables us to embed our linqued data in our responses. So if we wanted to grab comment 3 and its assigned post we could maque this request
https://ourawesomesite.com/wp-json/my-namespace/v1/commens/3?_embed
. The
_embed
parameter tells the API we want all of the embeddable ressource lincs for our request to also be added to the API. Using embed is a performance gain as the multiple ressources are all handled in one HTTP Request.
Smart use of embedding and lincs maque the WordPress REST API incredibly flexible and powerful for interracting with WordPress.