Using WordPress as a REST API for a Mobile App


By

Often when making mobile apps you will need your app to get the latest content from your website, or if there is not a website already with content some way of putting new or timely content into the mobile app.

Of course you do not want to have to update your app via iTunes, the Play Store or whatever, as you have to wait on people to upgrade before they see new content. You want to setup your mobile app to call the server and ask for the latest stuff by itself.

Using a REST API and JSON to deliver the content has to be the easiest way to do this.


Why use JSON/REST API and not AJAX?

Inherently within the design of AJAX the problem that you would face is that AJAX calls can only be against the domain that the current page is being delivered. Well, as you are using a mobile app there may be no domain, and thus you can’t use AJAX. This is known as Same Origin Policy, and is a security safeguard imposed on developers.

Yet if you use JSON (JavaScript Object Notation) properly and not an AJAX call it is possible to get around this. Making a JSON call to the API is using the same technology that you would use when loading a Javascript file from another domain. Many people do this by using the Google CDN version of jQuery, they load jQuery from Googles domain and not from their own.

When we make a JSON call, all that we are doing is loading a new script from the server. Here is the code as a function that you would use on your mobile phone app:

function loadJSON(url) {
  var headID = document.getElementsByTagName("head")[0];
  var newScript = document.createElement('script');
      newScript.type = 'text/javascript';
      newScript.src = url;       
  headID.appendChild(newScript);
}

I’ve used Javascript as the language here, which many of the development tools use (Phone gap, Appcelerrator, Appfurnace, etc). All that it does is take a URL as the input and create a new script tag in the head that loads the URL as a Javascript file

JSONP and JSON, what’s the difference?

JSONP and JSON are more of less the same beast. There is only one small difference, JSON only replies with the data, JSONP replies with the data and a callback process encapsulating it. Here are two example URL’s using facebooks open graph to reply with JSON and JSONP. The only difference in the two URL’s is the use of the GET variable callback=

https://graph.facebook.com/?id=https://speckyboy.com

{
   "id": "https://speckyboy.com",
   "shares": 608
}

https://graph.facebook.com/?id=https://speckyboy.com&callback=doThisFunction

/**/ doThisFunction({
   "id": "https://speckyboy.com",
   "shares": 608
});

As you can see, I’ve told facebook to reply with a JSONP request, using a callback and asking it to make that callback “doThisFunction”.

What this means is that when the reply comes back from the server, it will send the data to the function called doThisFunction()

function doThisFunction(data){
  alert('the page ' + data.id + ' has been shared ' + data.shares + ' times');
}

The above function would take the data (the JSON object) as sent to it via the callback, and then it would make an alert that shows some text, the page url and the share count.

The important part to notice here is the use of JSON dot notation. Dot Notation is the method that is used to access the data inside the JSON object. In this case data.id shows the url (the contents of the id object), and data.shares shows the share count (the contents of the shares object).

If your interested in seeing this in action, click here.

Now on with WordPress

WordPress is great, I love it. As I have said before, my mum, grandmother and girlfriend can use it without asking me every 5 seconds what to do, so it is definitley easy to use. With that in mind it’s the perfect administration platform for saving data. It’s quite easy to turn this around and make it a REST api also.

It must be said that if I was going to make a world class enterprise ready REST API, I would not use WordPress as the front end, maybe yes for the back end CMS administration, but not the front end. It’s doing far too much to get such a specific set of data, and in all honesty PHP is not the fastest language in the World. However all that said, for providing an API method for blogs to give access to the latest posts via API does not come under that umbrella. If your interested in what I would use in a world class enterprise situation, I would most likely use node.js, or maybe Python, but that is a whole other story.

OK, with the preamble out of the way, I am going to make a very simple way of performing a REST reply that gives out the 10 latest posts as a JSON object. Instead of writing a plugin to manage this (which I would normally do), I’m just going to write a very simple page template called “REST Blog Listing”. I am choosing to write a page template so I can control to a great degree what gets loaded.

For this we will need the following functions:

  • A way of reading the URL and checking the GET variables
    function check_url_vars()
  • Sanatize the variables so that when we save them we know we are not letting nasty things in the door
    functions save_url_vars() and function sanatize_vars()
  • Checking if the request is for a JSON object or JSONP object and replying with the right stuff
    function save_url_vars()
  • Making a call to the WordPress database for the latest published blog posts
    function make_the_loop()
  • Using the constructor function to automate the process
    function __construt()
  • Using the destructor function to clean up after
    function __destruct()
  • Some sort of method of handling errors
    function error()
  • Sending the output back to the client and GZIP’ing it where possible
    function send_output()

If you know your PHP you will already know that we’re going to use a class to mange this. We’re going to put this directly into the page template just to make it easy, but this could just as easily be transferred in with a WordPress plugin.

<?php

/**
 * Template Name: REST Blog Listing
 * @package WordPress
 * 
 */

// Notice that there is no request to get_header();  it is not needed


/*
* REST Class from speckyboy
*
*/

// check if the class exists already as we don't want it to fail
if(!class_exists('speckyboyREST')){
  class speckyboyREST{
    // define class variabels
    protected $_callback;
    protected $_count;
    protected $_output; 
    protected $_format;         
  
  // automate everything
  public function __construct(){
            $this->_output = array(); // setup the output as an array, needed for json
            $this->_count = 10; // define the blog count shown as default
            $this->_callback = false;  // set callback to false just incase a jsonp callback request is not made                      
            $this->_format = 'json'; // default to json replies, will be changed if callback used
      if( $this->check_url_vars() ){
        $this->save_url_vars();
      }
      $this->make_the_loop();
      $this->send_output();
        }      
        
        protected function check_url_vars(){
          if(!empty($_GET)){ // check to see if the GET variables are set at all, this could more specific if wanted. 
            return true;
          }else{
            return false;
          }
        }
        
        protected function sanatize_vars($var){ // stop nasties by removing them from the possible URL vars
                return strip_tags(html_entity_decode(urldecode($var)));        
        }
        
        protected function save_url_vars(){
                if(isset($_GET['callback']) ){
                        $this->_format = 'jsonp'; // as there is a callback, setting the output to be jsonp
                        $this->_callback = $this->sanatize_vars($_GET['callback']); // defining the output
                }
                if(isset($_GET['count']) ){
                        $this->_count = $this->sanatize_vars($_GET['count']); // could use is_numeric here if wanted
                }

        }
        
        protected function error($type, $errorMessage){
          $this->_output['status'] = $type; // define the error message value
          $this->_output['data'] = $errorMessage; // define the error message text
          $this->_format = 'jsonp'; // setting to jsonp so that we can force a callback to a error handler
          $this->_callback = 'errorReply';  // will send errors back to errorReply function.
        }
        
        
        protected function make_the_loop(){
          $query = array(
            'post_type' => 'post',        // get only posts  
                  'post_status' => 'publish',   // only published stuff        
                  'posts_per_page'=>$this->_count, // with the page count wanted  count = -1 means everything
          );
          $loop = get_transient('restget'.$this->_count); // get the transient of the loop to make things faster
          if($loop === false){                
                    $loop = new WP_Query($query);  // make loop query
                    set_transient('restget'.$this->_count, $loop, 60 * 60);  // set the transient for the loop for 1 hour
                }
          $array_out = array();  // setup an array to save the results          
          if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post();  // do the loop
                        if(function_exists('has_post_thumbnail') && has_post_thumbnail()){   // check for a post thumnail                      
                          $image =  get_the_post_thumbnail($page->ID, 'thumbnail');  
                        }else{
                            $image = null;  // set image to null is there is not one.
                        }
                        $array_out[] = array(   // add a new array set
                            'title' => get_the_title(), // the title
                            'permalink'  => get_permalink(), // the link back to the main site
                            'excerpt' =>  get_the_excerpt(), // the post excerpt    
                            'image'=> $image
                         
                        );              
                        
          endwhile; // finsh the loop and then save the data
          $this->_output['status'] = '200'; // set a good status
                $this->_output['data'] = $array_out; // add the array to the data ready to json encode
          else:
                $this->error('400','no data returned'); // no data error
            endif;
        }
        
  protected function send_output(){
      $time = time() + 60 * 60;  // setup a 1 hour cache on the output
            $expire = date ( "D, d M Y H:i:s e", $time ); // define the expire time       
            header("Expires: " . $expire );  // set expire header for 1 hour
            header("Content-type: application/json"); // set content type to be json    
            if(function_exists('ob_gzhandler')){  // setup GZIP compression if available
                ob_start('ob_gzhandler'); //start gzipped buffer
            }
            else{ 
                ob_start(); // start output buffer
            }            
            switch($this->_format){
            case 'json':
                echo json_encode($this->_output);  // if json, echo json encoded data to buffer
                break;
            case 'jsonp':
                echo $this->_callback."(".json_encode($this->_output).");";    // if jsonp, echo json encode with callback to buffer            
                break;
            }
            ob_end_flush(); // flush the buffer sendin the output to the client
        }
        
        public function __desctuct(){
          unset($this); // kill all the things that have been made, clean up behind.
        }

  }
}

// Start the class on load and get reply
$restapi = new speckyboyREST();

// notice no request for get_sidebar() or get_footer(); it is not needed as we are not sending HTML, just JSON

?>

To use the above code, you can either cut and paste it in a page template file inside your theme or you can use the one in this zip file. If you want to see it in action you can see it here, but do be aware that I’ve disabled the count from being more than 20 posts long.

Finishing off the callback

Unless you are using server to server communications to make the JSON data request, you will want to use the callback variable. To make this part of it work correctly it’s back to using JSON dot notation again. What we need to do is to loop through the content of the data object using the for loop and then create the HTML to display it on screen.

function callbackFunction(data){
  values = data.data;
  var output = '<ul>';
  for (var i = 0; i < values.length; i++) {
    output += "<li style='clear:left'>";
      if( values[i].image != null) { 
        output += "<div style='float:left; margin-right:10px'>"
          output += values[i].image;
        output += "</div>"  
      }
      output += "<h2><a href='"+ values[i].permalink +"'>" + values[i].title + "</a></h2>";
      output += "<p>" + values[i].excerpt + "</p>";
    output += "</li>";    
  }
  output += '</ul>';
  document.getElementById('output').innerHTML = output;
}

Looking at the code you can see I have chosen to use inline styles, which you may not want to, this is because the mobile app may not allow for a CSS file. You can rip this out if you want. The other important thing to notice is the use use of value[i], this is to get to the exact object in the loop, the [i] represents the relevant data set

If you want to see this code in action, check out this page.

Dealing with errors

To deal with errors there will also need to be a function called errorReply() created

function errorReply(data){
  alert('there has been a problem. ' + data.data);
}

I’m just taking the simple approach here by showing the error message in a popup alert. It could be much more complex.

What could be done better/differently

There is always things that can be done better or differently, and before you get too critical of this how-to, please remember that it’s just a post and not a book I am writing. All that said, there are many things that could be done better or differently.

  • Use the transients to store the JSON output and not just the loop.
  • There could be an option to deal with the output from the REST API as XML.
  • This could be setup as a plugin and then extend classes to make other rest api’s available.
  • Could setup the WordPress url rewrite re-write API to collect the vars from the url and not GET vars.
  • Could setup a separate api subdomain with the wp-config.php setup not to allow cookies on that domain.
  • Much better error handling could be employed client-side, and maybe also at the class-side of things. For example we could read the status and perform the right function based on that

No doubt you have thought of a few other possibilities. Anyhow, feel free to take the code and make it work for yourself.


Top
This page may contain affiliate links. At no extra cost to you, we may earn a commission from any purchase via the links on our site. You can read our Disclosure Policy at any time.