How to Make Custom Endpoints For REST API

How to Make Custom Endpoints For REST API

All versions of WordPress 4.4 and above support the much-anticipated custom endpoints for REST API.

HTTP REST API enables WordPress users to bypass the limitations of a WordPress website and interact with it remotely.

Users can define REST API endpoints, and data is transmitted and received in JSON (JavaScript Object Notation) format. JSON is an open data format that is easy to read and very lightweight.

In this article, we’ll provide a step-by-step tutorial on how to create custom endpoints for WordPress REST API.

The custom endpoints for REST API are 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 custom endpoints for 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,’ representing the type of action a client might perform for a particular resource. The ‘HTTP verbs’ standardize across the web.”

A ‘GET’ request fetches a resource, while a ‘DELETE’ request removes or deletes a specific 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 triggers an update operation, returning 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 operations available through the API.

They can perform tasks such as creating a post, adding a comment to a post, getting 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, they can take some number of parameters and return data to the client.

What is a Route?

Routes access required endpoints using reference URIs (Universal Resource Indicators).

How to Create a REST Endpoint?

You can register custom endpoints for REST API 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 custom endpoints for REST API.

Let’s take a more object-oriented approach. We’ll use PHP classes to add REST endpoints and also use the permissions callback (necessary when we want our private data 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. The 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 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: get_latest_post and add_post_to_category. We also use the permission_callback to define the respective endpoint’s permissions.
The constants of WP_REST_Server are given in the table below,

table
Constant NameConstant 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) when the condition check fails. We’ll check whether the current user can edit posts. If the user has the appropriate permissions, then the callback permission will 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 the 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 endpoints for REST API. Now, you can create and define your own REST routes for your WordPress websites. Pretty amazing, huh?
Happy Coding!

Conclusion

Understanding how to create custom endpoints for REST API in WordPress is key to unlocking advanced functionalities and enhancing user experiences. This tutorial has provided a comprehensive guide, from basic endpoint creation using hooks to more sophisticated approaches using PHP classes and permissions callbacks.

By leveraging HTTP verbs and WordPress core functions, developers can streamline data transmission and interaction with WordPress websites remotely. Testing these endpoints using tools like Postman ensures functionality and allows for refinement.

Mastering custom endpoints empowers developers to create dynamic, secure, and interactive applications, making WordPress a robust platform for modern web development.

5 Comments on “How to Make Custom Endpoints For 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.