How to make custom endpoints for WordPress REST API

How to make custom endpoints for WordPress REST API

All versions of WordPress 4.4 and above support the much-anticipated REST API.
HTTP REST API enables WordPress users to break the limitations of a WordPress website so that they can interact with their website remotely. Users can define endpoints of the REST API, and the data is transmitted and received in JSON (JavaScript Object Notation) format. JSON is an open data format that is easy to read and is very lightweight.

In this article, we’ll walk you through a step by step tutorial about how to make custom endpoints for WordPress REST API.

The REST API is included in the core of WordPress version 4.8.x. We’ll be demonstrating this tutorial using WordPress 4.8, but you can use any WordPress 4.7.x version. We’ve been using the same REST API in plugin/theme development since WordPress 4.7.

Note: For WordPress versions 4.4 and later, a separate plugin is needed to enable HTTP REST API.

Some terminologies before we get started –

What are GET, POST, PUT, DELETE and HEAD requests?

These are known as “HTTP verbs” which represent the type of action a client might perform for a particular resource. The ‘HTTP verbs’ are standardized across the web.
A ‘GET’ request might represent fetching action of a resource whereas a ‘DELETE’ request is used to remove/delete a particular request.

Let’s understand these terminologies with an example. Consider the following URL
http://example.com/wp-json/wp/posts/123/

The above URL contains the route ‘wp/posts/123’, which has multiple endpoints.
A ‘GET’ request returns a post with id 123 to the client.
A ‘POST’ request to the same route will trigger an update operation and return the newly updated post data to the client.
A ‘DELETE’ request will trigger a delete operation of the same post.

What is a Rest Endpoint?

Rest API Endpoints are the operations available through the API. They can perform tasks such as creating a post, adding a comment to a post, get all posts from a certain category, etc. These are just examples; you can define the endpoints to execute your functionalities. Since endpoints perform a certain task, it can take some number of parameters and can return data to the client.

What is a Route?

Routes are the reference URI’s (Universal Resource Indicator) used to access a required endpoint/endpoints.

How to create a REST endpoint?

You can register REST API endpoints in your theme or plugin. We’ll be using a child theme of ‘Twenty Seventeen’ to register the API endpoints.

== Basic ==

For creating a very small and simple API resource, you only need to use the ‘rest_api_init’ hook and the function ‘register_rest_route.’ Following is a basic demonstration:

  	<?php 
 
  	/**
  	 *
  	 * Get The latest post from a category !
  	 * @param array $params Options for the function.
 	   * @return string|null Post title for the latest,? * or null if none
  	 *
  	 */
 
 
  	 function get_latest_post ( $params ){
  	 	$post = get_posts( array(
          'category'      => $category,
            'posts_per_page'  => 1,
            'offset'      => 0
      ) );
 
  	 	if( empty( $post ) ){
  	 		return null;
  	 	}
 
  	 	return $post[0]->post_title;
  	 }
 
  	 // Register the rest route here.
 
  	 add_action( 'rest_api_init', function () {
            
            register_rest_route( 'mynamespace/v1', 'latest-post',array(
 
                'methods'  => 'GET',
                'callback' => 'get_latest_post'
 
            ) );
 
     } );

In this code, we’ve used the ‘rest_api_init’ hook to register a route with the ‘register_rest_route’ function. This will create an endpoint that is accessible from the following link,

http://example.com/wp-json/mynamespace/v1/latest-posts

After passing the GET parameter ‘category,’ It serves the title of the latest post in the given category.

http://example.com/wp-json/mynamespace/v1/latest-posts?category=art

will give results like:

create REST API endpoint

We have successfully created a REST endpoint using the WordPress REST API.

Let’s give it a more object oriented approach. We’ll be using PHP classes to add REST endpoints and also use the permissions callback (necessary when we want our private data to be displayed if the request can satisfy some conditions).

We will also be using the WordPress REST API constants. Using these constants is a standard approach and also ensures that when the WP_REST_Server (WordPress REST API class) changes our endpoints, it will work as intended.

Let’s go through the class and its member variables,

    <?php
      
       class My_Rest_Server extends WP_REST_Controller {
 
          //The namespace and version for the REST SERVER
          var $my_namespace = 'my_rest_server/v';
          var $my_version   = '1';
       }

The class My_Rest_Server is initialized as a child class of WP_REST_Controller. This follows the WordPress standards. The $my_namespace is used throughout the class so that we can have the same namespace for our REST routes. Same goes for the $version variable. If you want to update your REST server, you can simply bump up this number.

Let’s start registering the routes. We will create a REST route with multiple endpoints and also use permissions for both the endpoints.

    public function register_routes() {
      $namespace = $this->my_namespace . $this->my_version;
      $base      = 'category';
      register_rest_route( $namespace, '/' . $base, array(
        array(
            'methods'         => WP_REST_Server::READABLE,
            'callback'        => array( $this, 'get_latest_post' ),
            'permission_callback'   => array( $this, 'get_latest_post_permission' )
          ),
        array(
            'methods'         => WP_REST_Server::CREATABLE,
            'callback'        => array( $this, 'add_post_to_category' ),
            'permission_callback'   => array( $this, 'add_post_to_category_permission' )
          )
      )  );
    }

We’ve created two endpoints, namely, get_latest_post and add_post_to_category. We also use the permission_callback to define the permission of the respective endpoint.
The constants of WP_REST_Server are given in the table below,

table

Constant Name Constant Value

Let us now define a permission callback,

  public function get_latest_post_permission(){
    if ( ! current_user_can( 'edit_posts' ) ) {
          return new WP_Error( 'rest_forbidden', esc_html__( 'You don\'t have permissions to view this data.', 'my-text-domain' ), array( 'status' => 401 ) );
      }
 
      // This approach blocks the endpoint operation. You could alternatively do this by an unblocking approach, by returning false here and changing the permissions check.
      return true;
  }

The callback function returns an error (WP_Error) on the failure of the condition check. We’ll check whether the current user can edit posts and if the user has the appropriate permissions then does the callback permission return true. The callback can also return false if you want to deny access according to your condition.

Integrating all of the code,

<?php
 
class My_Rest_Server extends WP_REST_Controller {
 
  //The namespace and version for the REST SERVER
  var $my_namespace = 'my_rest_server/v';
  var $my_version   = '1';
 
  public function register_routes() {
    $namespace = $this->my_namespace . $this->my_version;
    $base      = 'category';
    register_rest_route( $namespace, '/' . $base, array(
      array(
          'methods'         => WP_REST_Server::READABLE,
          'callback'        => array( $this, 'get_latest_post' ),
          'permission_callback'   => array( $this, 'get_latest_post_permission' )
        ),
      array(
          'methods'         => WP_REST_Server::CREATABLE,
          'callback'        => array( $this, 'add_post_to_category' ),
          'permission_callback'   => array( $this, 'add_post_to_category_permission' )
        )
    )  );
  }
 
  // Register our REST Server
  public function hook_rest_server(){
    add_action( 'rest_api_init', array( $this, 'register_routes' ) );
  }
 
  public function get_latest_post_permission(){
    if ( ! current_user_can( 'edit_posts' ) ) {
          return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permissions to view this data.', 'my-text-domain' ), array( 'status' => 401 ) );
      }
 
      // This approach blocks the endpoint operation. You could alternatively do this by an un-blocking approach, by returning false here and changing the permissions check.
      return true;
  }
 
  public function get_latest_post( WP_REST_Request $request ){
    //Let Us use the helper methods to get the parameters
    $category = $request->get_param( 'category' );
    $post = get_posts( array(
          'category'      => $category,
            'posts_per_page'  => 1,
            'offset'      => 0
    ) );
 
      if( empty( $post ) ){
        return null;
      }
 
      return $post[0]->post_title;
  }
 
  public function add_post_to_category_permission(){
    if ( ! current_user_can( 'edit_posts' ) ) {
          return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permissions to create data.', 'my-text-domain' ), array( 'status' => 401 ) );
      }
      return true;
  }
 
  public function add_post_to_category( WP_REST_Request $request ){
    //Let Us use the helper methods to get the parameters
    $args = array(
      'post_title' => $request->get_param( 'title' ),
      'post_category' => array( $request->get_param( 'category' ) )
    );
 
    if ( false !== ( $id = wp_insert_post( $args ) ) ){
      return get_post( $id );
    }
 
    return false;
    
    
  }
}
 
$my_rest_server = new My_Rest_Server();
$my_rest_server->hook_rest_server();

Let’s test these REST routes. You can use Postman Chrome App to test out your REST endpoints. It can make various types of requests and has a user-friendly UI to test out our endpoints. Alternatively, you can also use cURL to test out your endpoints. For this tutorial, we are going to use PostMan.

The routes defined in the class above are,

GET   http://example.com/wp-json/my_rest_server/v1/category POST http://example.com/wp-json/my_rest_server/v1/category

For the GET endpoint with parameter category=art, we get the following result

WordPress REST API

WordPress REST API

For the GET endpoint with parameter category=art title=test, we get the following result

WordPress REST API

And for a permission error, we get the following,

WordPress REST API

You can use the above class as a boilerplate for your custom REST API. Now you can create and define your own REST routes for your WordPress websites. Pretty amazing huh?
Happy Coding!

5 Comments on “How to make custom endpoints for WordPress REST API

  1. Hello Mate,

    How can I overcome with permission issue. I am getting the following error:
    Method: GET
    url : http://localhost/akhnoor/wp-json/my/v2/category/

    {
    “code”: “rest_forbidden”,
    “message”: “You do not have permissions to view this data.”,
    “data”: {
    “status”: 401
    }
    }

    I want to implement OAuth2 with my custom endpoints.

    Please suggest. If you have any running example with OAuth.

  2. This guide helps you build api to use with Ajax calls. So in order to pass the authorization, one must be logged in.

    In fact:

    public function get_latest_post_permission(){
    if ( ! current_user_can( ‘edit_posts’ ) ) {
    return new WP_Error( ‘rest_forbidden’, esc_html__( ‘You do not have permissions to view this data.’, ‘my-text-domain’ ), array( ‘status’ => 401 ) );
    }

    // This approach blocks the endpoint operation. You could alternatively do this by an un-blocking approach, by returning false here and changing the permissions check.
    return true;
    }

    if you change it to

    public function get_latest_post_permission(){
    return true;
    }

    it allows to have a list of post with no authentication.

    Be aware that removing the authorization is not safe for saving posts (ie public function add_post_to_category_permission() ).

    I’ve changed the authorization process by adding an apikey (in https headers) but I don’t use in Ajax calls but in server-to-server services.

    Hope this helps!
    Aisfrond

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.