Google API clients: Why you should use them and how

Google API clients: Why you should use them and how

Some of our older code examples, including many that I have authored on my blog have been using raw query execution against the Google APIs. This is undesirable and is absolutely not a best practice because you will not benefit from the convenience and reliability that comes with using methods on the API.

The beauty of the Google APIs is that they use a discovery service that allows Google and third parties to generate updated packages on the fly for developers to consume in their target languages. What this means is that as the APIs change, the client libraries will automatically be updated to support these changes. Furthermore, these client libraries are owned and tested by Google making them much more stable and reliable than using raw access to the API endpoints.

In this post, I’ll show you first a few bad examples for queries and will show you better ways of accessing Google’s APIs.

The first culprit: client request execution

The Google API clients support performing raw execution against the Google APIs using appropriate network stacks for that particular client.  This is great design because client queries can share the same execution code and benefit from improvements in caching, performance, reliability, and so forth. Additionally, the API will manage authorization and will manage your bearer token for you.  However, accessing the APIs in this manner requires you to parse and manage JSON objects which can introduce bugs and support issues for you, the developer.

For example, in this post, I run the following code (do not do this):

var payload = {
  "target": {
    "id" : "replacewithuniqueidforaddtarget",
    "image" : "http://www.google.com/s2/static/images/GoogleyEyes.png",
    "type" : "http://schema.org/CreativeWork",
    "description" : "The description for the activity",
    "name":"An example of AddActivity"
  },
  "type":"http://schemas.google.com/AddActivity",
  "startDate": "2012-10-31T23:59:59.999Z"
};
var args = {
  'path': '/plus/v1/people/me/moments/vault',
  'method': 'POST',
  'body': JSON.stringify(payload),
  'callback': function(response) {
     console.log(response);
   }
};

gapi.client.request(args);

What this code is doing is directly POSTing JSON data to the API endpoint. This is undesirable because there are a number of ways that things can go wrong and you must write loads of unnecessary and difficult to read code. In addition, you have to stringify the API call when you make it and also parse the response from the query.

Next culprit: XHR / RAW HTTP requests

This is actually worse than the first culprit: rolling your own client using the XHR object in JavaScript. The following example, from code I wrote roughly a year ago, illustrates what not to do (DO NOT DO THIS):

    // globals used for auth, showing debugging
    var debug = true;
    var key   = "AIzaSyC63QTL6Brw_4IQFK6uKRQDj-KmGPKMwnA";

    function handleRequestIssue(request){
        // For now, just can cry about it..
        console.log("khhhhaaaaann!!!, status:" + request.status + " /  response:" + request.responseText);
    }

    // Gets the activities for a profile
    function getActivities(profileID){
      var activities = null;      
      var URL        = "https://www.googleapis.com/plus/v1/people/" + profileID + "/activities/public?alt=json&key=" + key;
      var request = new XMLHttpRequest();
      request.open('GET', URL, false);
      request.send(); // because of "false" above, will block until the request is done 
                      // and status is available. Not recommended, however it works for simple cases.

      if (request.status === 200) {
        if (debug) console.log("retrieved activities nn");
        if (debug) console.log("rawnn" + request.responseText);
        activities = jQuery.parseJSON(request.responseText).items;
        console.log("Discovered " + activities.length + " activities");
        console.log("raw:" + request.responseText);
      }else{
        handleRequestIssue(request);
      }

      return activities;
    }

    function getCommentsForActivity(activityID){
      var comments = "";      
      var URL        = "https://www.googleapis.com/plus/v1/activities/" + activityID + "/comments?alt=json&key=" + key;
      var request = new XMLHttpRequest();
      request.open('GET', URL, false);
      request.send(); // because of "false" above, will block until the request is done 
                      // and status is available. Not recommended, however it works for simple cases.

      if (request.status === 200) {
        if (debug) console.log(request.responseText);
        comments  = jQuery.parseJSON(request.responseText).items;

        if (debug){
          for (comment in comments){
            console.log(comment);
          }
        } 

      }else{
        handleRequestIssue(request);
      }

      return comments;
    }

    function manualTrigger(){
      var activities = getActivities("109716647623830091721");
    }

As you can see, this code is even less readable than the first set of code! I’m even adding an ugly and blatantly terrible error handler that lacks remediation for the response issues. The code is super fragile: changes at multiple points in the process will introduce bugs in my demo. Do not access the APIs this way.

Doing it RIGHT: The Google API Clients

Here is the best way to do this: using the Google API clients and accessing the functionality and data through the API client.  The following JavaScript example lists comments from the current user’s stream and can be seen here: Demo: listing comments (DO IT THIS WAY):

<html>
<script>
    var output;

    function signinCallback(result){
        console.log(result);
        if (result.access_token != null){
          // success
          document.getElementById('signinButton').style.display = 'none';
          output = document.getElementById('output');
        }
        // Load the client library and call getActivities once it's loaded
        gapi.client.load('plus', 'v1', getActivities);
    }

    // Gets the activities for a profile using the client library
    function getActivities(){
      var activities = null;
      gapi.client.plus.activities.list({'userId' : 'me'}).execute(
          function(activities){
            console.log(activities);
            for (var index = 0; index < activities.items.length; index++){
              output.value += 'nn-------n' + JSON.stringify(activities.items[index]);
              output.innerText = output.innerText + JSON.stringify;
              getCommentsForActivity(activities.items[index].id);
            }
          });
    }

    function getCommentsForActivity(activityID){
        gapi.client.plus.comments.list(
            {'activityId' : activityID}).
            execute(
                function(comment){
                  output.value += 'n' + JSON.stringify(comment);
                  console.log(comment);
                });
    }
</script>
<body>

  <span id="signinButton">
    <span
      class="g-signin"
      data-callback="signinCallback"
      data-clientid="1065047579848.apps.googleusercontent.com"
      data-cookiepolicy="single_host_origin"
      data-scope="https://www.googleapis.com/auth/plus.login">
    </span>
  </span>
  <textarea cols="80" rows="40" id="output">
  </textarea>
  <!-- Place this asynchronous JavaScript just before your </body> tag -->
  <script type="text/javascript">
    (function() {
     var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
     po.src = 'https://apis.google.com/js/client:plusone.js';
     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
   })();
  </script>
</body>
</html>

See how much better it is!  This code is even doing MORE than the other code in fewer lines.  You get nice objects and methods for all of the operations performed on Google+ and the code will be updated from Google if anything ever changes. By accessing the API in this manner, you will be getting all of the client library updates by loading the client library from Google and will get a reliable means of accessing the API functionality.

The Google+ quickstarts show how to use the client libraries in a number of languages, this is a great way to get started with understanding the relevant patterns for your language of choice. Also, this is a great way to learn how to perform the operations in a language you understand less because the samples are virtually identical in all languages.

Conclusion

Although it may seem obvious now, always make sure that you use the Google API clients when programming against the Google APIs. There is a client for virtually every language out there to make your code more maintainable, stable, performant, and readable. The API clients support much more than I covered here that makes your life easier. Many other great examples of client execution can be found on Ian Barber’s blog.  For example, the following article shows how to use the API client to batch API calls:

The generic Google client libraries that use the discovery service can be found at:

You can also download the Google+ API clients from the Google+ developer pages that are targeted to Google+.

For certain languages such as C# and Java, the client libraries must be generated, so occasionally it may be a good idea to update your clients if you’re using these languages. Have fun accessing the APIs, and be sure to do it the right way, using the freely available clients!