{"id":1757,"date":"2020-07-16T18:57:22","date_gmt":"2020-07-16T11:57:22","guid":{"rendered":"https:\/\/wptips.dev\/?p=1757"},"modified":"2020-11-20T21:07:25","modified_gmt":"2020-11-20T14:07:25","slug":"custom-rest-api","status":"publish","type":"post","link":"https:\/\/pixelstudio.id\/blog\/custom-rest-api\/","title":{"rendered":"How to Make Custom REST API (Beginner&#8217;s Guide)"},"content":{"rendered":"\n<p>REST API is a special set of URLs that we can call to get or save data. These URLs are commonly called endpoint.<\/p>\n\n\n\n<p>Every WordPress site comes with some <strong>default API endpoints<\/strong> like this one for getting recent posts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts<\/code><\/pre>\n\n\n\n<p>Try copy-pasting that to your browser and you will see a bunch of data in <strong>JSON format<\/strong>. This format can easily be converted into an Array in any programming language.<\/p>\n\n\n\n<p>In this tutorial we will learn how to make a custom endpoint.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Register GET Route<\/h2>\n\n\n\n<p>GET Request means <strong>receiving data<\/strong> from the server.<\/p>\n\n\n\n<p>In this example, we want 2 new endpoints for our Project post-type:<\/p>\n\n\n\n<p class=\"has-light-blue-background-color has-background has-small-font-size\"><strong>Note<\/strong>: If you would like to follow along with this tutorial, <a rel=\"noreferrer noopener\" href=\"https:\/\/gist.github.com\/hrsetyono\/97e89a1ea732d5661ab1f74a2c23fe9d\" target=\"_blank\">Paste in these code<\/a> in <code>functions.php<\/code> to generate Project post-type and create some initial data.<\/p>\n\n\n\n<p><strong>1. Static Route<\/strong> at <code>\/projects<\/code> to get recent projects:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action( 'rest_api_init', function() {\n  register_rest_route( 'my\/v1', '\/projects', [\n    'methods' => 'GET',\n    'callback' => 'get_projects',\n    'permission_callback' => '__return_true',\n  ] );\n} );\n\n\/\/ Get all projects and assign thumbnail\nfunction get_projects( $params ) {\n  $projects =  get_posts( [\n    'post_type' => 'project',\n    'posts_per_page' => 10\n  ] );\n\n  foreach( $projects as &amp;$p ) {\n    $p->thumbnail = get_the_post_thumbnail_url( $p->ID );\n  }\n\n  return $projects;\n}<\/code><\/pre>\n\n\n\n<p>The snippet above will create an endpoint at <strong>https:\/\/yoursite.com\/wp-json\/my\/v1\/projects<\/strong>.<\/p>\n\n\n\n<p>You might wonder what is <code>my\/v1<\/code>. That is the <strong>namespace<\/strong> and <strong>version<\/strong>.<\/p>\n\n\n\n<p>The namespace is to identify a group. You can use anything, but try to keep it short.<\/p>\n\n\n\n<p>The version is to differentiate an updated route. When someday you want to update this API, you should keep the <code>v1<\/code> as is and create a new route with <code>v2<\/code> instead. This is to prevent existing apps that are using v1 from breaking down.<\/p>\n\n\n\n<p><strong>2. Dynamic Route<\/strong> at <code>\/project\/[id]<\/code> to get a specific project:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action( 'rest_api_init', function() {\n  register_rest_route( 'my\/v1', '\/project\/(?P&lt;id>\\d+)', [\n    'methods' => 'GET',\n    'callback' => 'get_project',\n    'permission_callback' => '__return_true',\n  ] );\n} );\n\n\/\/ Get single project\nfunction get_project( $params ) {\n  $project = get_post( $params['id'] );\n  $project->thumbnail = get_the_post_thumbnail_url( $project->ID );\n  return $project;\n}<\/code><\/pre>\n\n\n\n<p>The breakdown of <code>(?P&lt;id&gt;\\d+)<\/code>:  <\/p>\n\n\n\n<ul><li><code>?P&lt;id&gt;<\/code> means it will save the value as &#8216;id&#8217;.<\/li><li><code>\\d+<\/code> is the regex validation and means it only accepts numbers. You can read more about regex <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Guide\/Regular_Expressions\/Cheatsheet\" target=\"_blank\">in MDN article here<\/a>.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Register POST Route<\/h2>\n\n\n\n<p>POST Request means <strong>sending data<\/strong> to the server.<\/p>\n\n\n\n<p>Continuing from the example above, we need a <strong>Search endpoint<\/strong> where we can filter by submitting title and\/or category.<\/p>\n\n\n\n<p>Here&#8217;s how:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action( 'rest_api_init', function() {\n  register_rest_route( 'my\/v1', '\/projects_search', [\n    'methods' => 'POST',\n    'callback' => 'post_projects_search',\n    'permission_callback' => '__return_true',\n  ] );\n} );\n\n\/\/ Search projects\nfunction post_projects_search( $request ) {\n  \/\/ Get sent data and set default value\n  $params = wp_parse_args( $request->get_params(), [\n    'title' => '',\n    'category' => null\n  ] );\n\n  $args = [\n    'post_type' => 'project',\n    's' => $params['title'],\n  ];\n\n  if( $params['category'] ) {\n    $args['tax_query'] = [[\n      'taxonomy' => 'project_category',\n      'field' => 'id',\n      'terms' => $params['category']\n    ]];\n  }\n\n  return get_posts( $args );\n}<\/code><\/pre>\n\n\n\n<p>It&#8217;s difficult to use your browser to simulate POST requests. So I suggest using a software called Postman.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Debugging with Postman<\/h2>\n\n\n\n<p>Postman is an amazing free software to try out API requests. First, download and register for an account:<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-blue-background-color has-background\" href=\"https:\/\/www.postman.com\/downloads\/\" target=\"_blank\" rel=\"noreferrer noopener\">Download Postman<\/a><\/div>\n<\/div>\n\n\n\n<p>After that, create a new POST request and enter the appropriate data. The screenshot below is the Postman setting for our Search endpoint:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"240\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-config.jpg\" alt=\"\" class=\"wp-image-1780\" srcset=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-config.jpg 750w, https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-config-480x154.jpg 480w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><figcaption>Postman config for POST request<\/figcaption><\/figure>\n\n\n\n<p>Click SEND and you will see the returned data in the panel below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"297\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-result-1.jpg\" alt=\"\" class=\"wp-image-1782\" srcset=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-result-1.jpg 750w, https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/07\/rest-api-postman-result-1-480x190.jpg 480w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><figcaption>The response from POST request above.<\/figcaption><\/figure>\n\n\n\n<p class=\"has-light-red-background-color has-background\" style=\"font-size:14px\">If there&#8217;s an error, move to &#8220;Preview&#8221; tab to see the error message in a clearer way.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create a Wrapper Class (Optional)<\/h2>\n\n\n\n<p>If the endpoints are related, I recommend putting them in a class. Not only it&#8217;s tidier it also relieves you from worrying about duplicate method names.<\/p>\n\n\n\n<p>Here&#8217;s what it looks like when grouping the 3 endpoints above:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">if( !class_exists( 'MyAPI' ) ) {\n\nclass MyAPI {\n  function __construct() {\n    add_action( 'rest_api_init', [$this, 'init'] );\n  }\n\n  function init() {\n    register_rest_route( 'my\/v1', '\/projects', [\n      'methods' => 'GET',\n      'callback' => [$this, 'get_projects'],\n    ] );\n\n    register_rest_route( 'my\/v1', '\/project\/(?P&lt;id>\\d+)', [\n      'methods' => 'GET',\n      'callback' => [$this, 'get_project'],\n    ] );\n\n    register_rest_route( 'my\/v1', '\/projects_search', [\n      'methods' => 'POST',\n      'callback' => [$this, 'post_projects_search']\n    ] );\n  }\n\n  \/\/ Get recent projects\n  function get_projects( $params ) {\n    $projects =  get_posts( [\n      'post_type' => 'project',\n      'posts_per_page' => 10\n    ] );\n\n    foreach( $projects as &amp;$p ) {\n      $p->thumbnail = get_the_post_thumbnail_url( $p->ID );\n    }\n\n    return $projects;\n  }\n\n  \/\/ Get single project\n  function get_project( $params ) {\n    $project = get_post( $params['id'] );\n    $project->thumbnail = get_the_post_thumbnail_url( $project->ID );\n    return $project;\n  }\n\n  \/\/ Search projects\n  function post_projects_search( $request ) {\n    \/\/ Get sent data and set default value\n    $params = wp_parse_args( $request->get_params(), [\n      'title' => '',\n      'category' => null\n    ] );\n\n    $args = [\n      'post_type' => 'project',\n      's' => $params['title'],\n    ];\n\n    if( $params['category'] ) {\n      $args['tax_query'] = [[\n        'taxonomy' => 'project_category',\n        'field' => 'id',\n        'terms' => $params['category']\n      ]];\n    }\n\n    return get_posts( $args );\n  }\n}\n\nnew MyAPI();\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>WordPress REST API can be very powerful. It has been used by many plugins like WooCommerce to create an interactive experience.<\/p>\n\n\n\n<p>The Gutenberg editor also used the REST API in many areas such as updating your post without a page refresh.<\/p>\n\n\n\n<p>In my next few posts, I will cover about using JavaScript to call these APIs. This will lead to <strong>Headless WordPress<\/strong>, a topic most requested by our readers \ud83d\ude42<\/p>\n\n\n\n<p><strong>Useful links:<\/strong><\/p>\n\n\n\n<ul><li>List of default API endpoints &#8211; <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.wordpress.org\/rest-api\/reference\/\" target=\"_blank\">wordpress.org<\/a><\/li><li>Postman Download &#8211; <a rel=\"noreferrer noopener\" href=\"https:\/\/www.postman.com\/downloads\/\" target=\"_blank\">postman.com<\/a><\/li><li>The official tutorial for REST API &#8211; <a href=\"https:\/\/developer.wordpress.org\/rest-api\/extending-the-rest-api\/adding-custom-endpoints\/\">wordpress.org<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Wordpress REST API is a very powerful tool with the right implementation. Learn how to create custom GET and POST request here.<\/p>\n","protected":false},"author":1,"featured_media":1788,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[46,31],"class_list":["post-1757","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-general","tag-api","tag-php"],"blocksy_meta":"","_links":{"self":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/comments?post=1757"}],"version-history":[{"count":10,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1757\/revisions"}],"predecessor-version":[{"id":1895,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1757\/revisions\/1895"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media\/1788"}],"wp:attachment":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media?parent=1757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/categories?post=1757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/tags?post=1757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}