Using the JavaScript Client Library (v2.0)

Warning: This page is about Google's older APIs, the Google Data APIs; it's relevant only to the APIs that are listed in the Google Data APIs directory, many of which have been replaced with newer APIs. For information about a specific new API, see the new API's documentation. For information about authorizing requests with a newer API, see Google Accounts Authentication and Authorization.

This document describes how to use the JavaScript client library to send Google Data API queries and interpret returned responses.

Google provides a set of client libraries, in a variety of programming languages, for interacting with services that have data APIs. Using these libraries, you can construct API requests, send them to a service, and receive responses.

This document provides some general information about using the JavaScript client library, along with a set of examples of common uses.

Audience

This document is intended for JavaScript programmers who want to write client applications that can interact with Google Data services.

This document assumes that you understand the general ideas behind the Google Data APIs protocol. It also assumes that you know how to program in JavaScript.

For reference information about the classes and methods provided by the client library, see the JavaScript client library API reference (in JSdoc format).

This document is designed to be read in order; each example builds on earlier examples.

Terms of use

You agree to abide by the Google JavaScript Client Library Terms of Use when using the JavaScript client library.

Data model and control flow overview

The JavaScript client library uses a set of classes to represent the elements used by the Google Data APIs.

Note: The underlying representation of the data is JSON, but the client library provides an abstraction layer so you don't have to work with the JSON data directly. If you want to work directly with JSON, without the client library, see Using JSON with Google Data APIs.

The library provides methods that let you asynchronously send data to and receive data from a service that has a data API. For example, the google.gdata.calendar.CalendarService.getEventsFeed() method sends a request for a feed to Google Calendar. One of the parameters you pass is a continuation function, also known as a callback; the service returns the feed, in JSON format, by calling the continuation function. The client can then call various get methods to use the data in the form of JavaScript objects.

To add a new entry, you create the entry using the client library's classes and methods, then call the feed.insertEntry() method to send the new entry to the service. Again you provide a continuation function, which the service calls when the entry has been successfully added.

If you're new to JavaScript, the control flow may be a little confusing. After calling a method like getEventsFeed() or insertEntry(), in most cases your script ends. Execution resumes in the continuation function when the service returns the requested data. Therefore, anything that your client does to the returned data should be done in the continuation function, or called from that function. You may need to make some variables global in order to use them in multiple functions.

For more information about this style of programming, see "Continuation-passing style" in Wikipedia.

About supported environments

Currently, we only support JavaScript client applications that run in a web page in a browser. Currently supported browsers are:

  • Firefox 2.x & 3.x
  • Internet Explorer 6, 7, & 8
  • Safari 3.x & 4.x
  • Google Chrome (all versions)

The JavaScript client library handles all communication with the service's server. If you're an experienced JS developer, you may be thinking, "But what about the same origin policy?" The JavaScript client library allows your client to send Google Data requests from any domain while remaining compliant with the browser security model.

For an overview on authenticating with the Google Data APIs, see the Google Data APIs Authentication Overview. The rest of this document assumes that you're familiar with the basics of how this system works.

Sample client applications

To see the JavaScript client library in action, visit our samples page.

Tutorial and examples

The following examples show how to send various data API requests using the JavaScript client library.

To make them more concrete, these examples show how to interact with a specific service: Google Calendar. We'll point out places where Calendar differs from other Google services, to help you adapt these examples for use with other services. For more information about Calendar, see the Google Calendar Data API document.

Loading the library

Before your client can use the client library, the client has to request the client library code from the server.

Start by using a <script> tag in the <head> section of your HTML document to fetch the Google AJAX API loader:

<script type="text/javascript" src="https://www.google.com/jsapi"></script>

You can minimize roundtrips to Google's servers and decrease latency by preloading the library. To preload certain packages directly from the Google AJAX API loader (without using google.load() see below), use the following:

<script type="text/javascript"
      src="https://www.google.com/jsapi?autoload=%7Bmodules%3A%5B%7Bname%3Agdata%2Cversion%3A2.x%2Cpackages%3A%5Bblogger%2Ccontacts%5D%7D%5D%7D"></script>

Note: The script's src URL should be fully urlencoded. For example, the previous example is
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={modules:[{name:gdata,version:2.x,packages:[blogger,contacts]}]}"></script>.

If you are not autoloading modules, you can load the Google Data client library by using the next example in your JavaScript setup code, after fetching the common loader. This call must be made from the <head> section of your HTML document (or from a JavaScript file that's included using a <script> tag in the <head> section of your HTML document):

google.load("gdata", "2");

Alternatively, you can request specific services instead of the entire library. This example downloads only the packages for Blogger and Contacts:

google.load("gdata", "2.x", {packages: ["blogger", "contacts"]});

The second parameter to google.load() is the requested version number of the JavaScript client library.  Our version numbering scheme is modeled after the one used by the Google Maps API. Here are the possible version numbers and what they mean:

"1"
The second-to-last revision of major version 1.
"1.x"
The very latest revision of major version 1.
"1.s"
The latest stable revision of major version 1. We will occasionally declare a certain version of the client library to be "stable," based on feedback we receive from developers. However, that version may not have the latest features.
"1.0", "1.1", etc
A specific version of the library, with a specified major and minor revision number.

After you've called google.load(), you have to tell the loader to wait until the page finishes loading and then call your code:

google.setOnLoadCallback(getMyFeed);

Where getMyFeed() is a function defined in the next section of this document. Use this approach instead of having an onload handler attached to the <body> element.

Requesting an unauthenticated feed

To request an unauthenticated feed, add the following code to your JavaScript file, or to a <script> tag in your HTML file.

In the following code, getMyFeed() is called first (by the AJAX API loader, as described in the previous section).

It calls setupMyService() to create a connection (represented by a CalendarService object) to Google Calendar. We've pulled the service creation code out into a separate function for modularity; later, we'll modify the setupMyService() function, depending on your authentication choices.

After setting up the service, getMyFeed() calls the client library's getEventsFeed() method to request the feed.

We're specifying the feed URL in a global variable so that it can be used in later functions. In this example, we're using the public (unauthenticated) feed URL for a user named [email protected] You can also use default in place of the user's email address to represent the authenticated user.

var feedUrl = "http://www.google.com/calendar/feeds/[email protected]/public/full";

function setupMyService() {
  var myService = new google.gdata.calendar.CalendarService('exampleCo-exampleApp-1');
  return myService;
}

function getMyFeed() {
  myService = setupMyService();

  myService.getEventsFeed(feedUrl, handleMyFeed, handleError);
}

Note that we're making myService a global variable, for ease of use in later functions.

To make the above code work in your own client, you have to use a real user's email address, for a Calendar account with a publicly shared calendar.

Note: When you create a new CalendarService object, the client library calls a method named google.gdata.client.init(), which checks that the browser the client is running in is supported. If there's an error, then the client library displays an error message to the user. If you want to handle this sort of error yourself, then you can explicitly call google.gdata.client.init(handleInitError) before you create the service, where handleInitError() is your function. If an init error occurs, then your function receives a standard Error object; you can do whatever you want with that object.

In the call to getEventsFeed(), the second argument is handleMyFeed, which is a callback function; see below. Google Calendar processes the request and then, if the request was successful, passes a "feed root" object containing the requested feed to the callback. A feed root is a container object that contains a feed.

The third argument to getEventsFeed() is an optional error-handling function; if the client library encounters an error, it calls the specified error handler instead of the success callback function. The object that the client library passes as the argument to the error handler is an instance of the JavaScript Error object, with an additional cause property.

Here are simple versions of the callback function and the error-handler:

function handleMyFeed(myResultsFeedRoot) {
  alert("This feed's title is: " + myResultsFeedRoot.feed.getTitle().getText());
}

function handleError(e) {
  alert("There was an error!");
  alert(e.cause ? e.cause.statusText : e.message);
}

We're handling errors by simply displaying them to the user; your client's error handler should probably be more sophisticated. In some contexts, there may be no cause specified, so in those cases our example error handler falls back to displaying the standard message property.

Note that since this code doesn't do authentication, you can use it only to get a public feed.

Authenticating

The JavaScript client library can be used in two modes. If you're writing a gadget, then it uses a feature called the OAuth Proxy for authentication. If it is being accessed from a standalone JavaScript application, it uses the AuthSub authentication system. For information about authentication, see the Google Data APIs Authentication Overview document. The rest of this section assumes that you're familiar with the basics of how this system works.

Before using authentication with the sample code provided in this document, change the feed URL from public to private:

var feedUrl = "http://www.google.com/calendar/feeds/[email protected]/private/full";

Authenticating in a web client with AuthSub

Google's "AuthSub for JavaScript" authorization system is no longer available.

Instead, we recommend using OAuth 2.0 for client-side applications.

Authenticating in a Gadget with the OAuth Proxy

Here's a brief overview of what happens during the authentication process for a gadget:

  1. Your gadget loads for the first time and attempts to access the user's data using one of the Google Data APIs.
  2. The request fails because the user hasn't granted access to their data, yet. The response object contains a URL (in response.oauthApprovalUrl) for the OAuth approval page. Your gadget should provide a method to launch a new window with that URL.
  3. On the approval page, the user chooses to grant/deny access to your gadget. If successful, the user is taken to the oauth_callback page you specify. For the best user experience, use http://oauth.gmodules.com/gadgets/oauthcallback.
  4. Next, the user closes the popup window. To help notify your gadget that the user has given the approval, we have provided a popup handler which you can use to detect the approval window closing. Alternatively, your gadget can display a link (e.g. "I've approved access") for the user to manually click after this window closes.
  5. Your gadget attempts to access the Google Data API a second time by re-requesting the user's data. This attempt is successful.
  6. Your gadget is authenticated and can begin operating normally.

In your gadget, add an <OAuth> element in the <ModulePrefs> section:

<ModulePrefs>
...
<OAuth>
  <Service name="google">
    <Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" /> 
    <Request url="https://www.google.com/accounts/OAuthGetRequestToken?
                  scope=http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/" method="GET" /> 
    <Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?
                        oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" /> 
  </Service>
</OAuth>
...
</ModulePrefs>

In this section, change the following query parameters:

  • scope

    A required parameter in the Request URL. Your gadget will only be able to access data from the scope(s) used in this parameter. In this example, the gadget will access your Blogger and Calendar data. A gadget can request data for a single scope or multiple scopes (as this example does).

  • oauth_callback

    An optional parameter in the Authorization URL. The OAuth approval page will redirect to this URL after the user has approved access to their data. You can choose to leave off this parameter, set it to your own "approved page", or preferably, use http://oauth.gmodules.com/gadgets/oauthcallback. The later provides the best user experience when users first install your gadget. That page provides a snippet of javascript that automatically closes the popup window.

Next, load the Javascript client library in the <Content> section of your gadget. Modify the setupMyService() function from the previous examples to call the service object's useOAuth() method. This tells the gadget to use the OAuth Proxy to authenticate rather than AuthSub. This template below should get you started:

<Content type="html">
<![CDATA[
  ...
  <script src="https://www.google.com/jsapi"></script>
  <script type="text/javascript">
    var myService = null;
    
    function setupMyService() {
      myService = new google.gdata.calendar.CalendarService('exampleCo-exampleApp-1');
      myService.useOAuth('google');
      fetchData();
    }
    
    function initGadget() {
      google.load('gdata', '2.x');
      google.setOnLoadCallback(setupMyService);
    }

    function fetchData() {            
      var callback = function(response) {
        if (response.oauthApprovalUrl) {
        
          // TODO: Display "Sign in" link (response.oauthApprovalUrl contains the URL) 
          
        } else if (response.feed) {
        
          // TODO: show results
          
        } else {
        
          // TODO: handle the error
          
        }
      };

      myService.getEventsFeed('http://www.google.com/calendar/feeds/default/public/full', callback, callback);
    }
    
    gadgets.util.registerOnLoadHandler(initGadget);
  </script>
  ...
]]> 
</Content>

Notice that the call to google.accounts.user.login(scope) has been removed. The Proxy handles the authentication for you.

For more information on writing Google Data API gadgets, including details of what fetchData() should contain, see our article on Creating a Google Data Gadget or check out the full Writing OAuth Gadgets documentation.

Inserting a new item

To create a new calendar event, continue execution from the earlier example by modifying the end of the handleMyFeed() function to call a new function:

function handleMyFeed(myResultsFeedRoot) {
  alert("This feed's title is: " + myResultsFeedRoot.feed.getTitle().getText());
  insertIntoMyFeed(myResultsFeedRoot);
}

In the new function, first use the CalendarEventEntry constructor to create the new entry. Then insert the entry, providing a callback for the service to call when the insertion is done.

function insertIntoMyFeed(feedRoot) {
  var newEntry = new google.gdata.calendar.CalendarEventEntry({
      authors: [{
        name: "Elizabeth Bennet",
        email: "[email protected]"
      }],
      title: {
        type: 'text', 
        text: 'Tennis with Darcy'
      },
      content: {
        type: 'text', 
        text: 'Meet for a quick lesson'
      },
      locations: [{
        rel: "g.event",
        label: "Event location",
        valueString: "Netherfield Park tennis court"
      }],
      times: [{
        startTime: google.gdata.DateTime.fromIso8601("2007-09-23T18:00:00.000Z"),
        endTime: google.gdata.DateTime.fromIso8601("2007-09-23T19:00:00.000Z")
      }]
  });
  feedRoot.feed.insertEntry(newEntry, handleMyInsertedEntry, handleError);
}

Note that the name of each object property used in the constructor matches the name of the setter method used for that property. (Rather than, for example, matching the corresponding JSON field name.)

Also note that you can't just provide ISO 8601 date-and-time strings for startTime and endTime; you have to run such strings through the fromIso8601() method first.

The service returns a copy of the inserted entry as an entryRoot object, and passes that object to the callback:

function handleMyInsertedEntry(insertedEntryRoot) {
  alert("Entry inserted. The title is: " + insertedEntryRoot.entry.getTitle().getText());
  alert("The timestamp is: " + insertedEntryRoot.entry.getTimes()[0].startTime);
}

Requesting a specific entry

To request a specific entry, first modify the handleMyInsertedEntry() function to call a new entry-request function:

function handleMyInsertedEntry(insertedEntryRoot) {
  alert("Entry inserted. The title is: " + insertedEntryRoot.entry.getTitle().getText());
  alert("The timestamp is: " + insertedEntryRoot.entry.getTimes()[0].startTime);
  requestMySpecificEntry(insertedEntryRoot.entry.getSelfLink().getHref());
}

The following code lets you request the specific entry that you inserted in the previous example.

In the context of this series of examples, retrieving that entry isn't really necessary, because Calendar already returned the inserted entry; but the same technique can be applied whenever you know the URI for an entry.

function requestMySpecificEntry(entryURI) {
  myService.getEventsEntry(entryURI, handleMySpecificEntry, handleError);
}

function handleMySpecificEntry(retrievedEntryRoot) {
  myEntryRoot = retrievedEntryRoot; // Global variable for later use
  alert("This entry's title is: " + retrievedEntryRoot.entry.getTitle().getText());
}

This example is essentially the same as the getEventsFeed() example, except that we're calling the service's getEventEntry() method to get a specific entry, and the URI is a little different—it uses "default" to refer to the main calendar for the authenticated user, and it has an entry ID at the end of it.

Also, we need to be able to use the retrieved entry later, so we're copying it into a global variable.

Searching entries

To perform a full-text search, first modify the handleMySpecificEntry() function to call a new search function:

function handleMySpecificEntry(retrievedEntryRoot) {
  myEntryRoot = retrievedEntryRoot; // Global variable for later use
  alert("This entry's title is: " + retrievedEntryRoot.entry.getTitle().getText());
  searchMyFeed();
}

Then to retrieve the first match from the search, use the following code:

function searchMyFeed() {
  var myQuery = new google.gdata.calendar.CalendarEventQuery(feedUrl);
  myQuery.setFullTextQuery("Tennis");
  myQuery.setMaxResults(10);
  myService.getEventsFeed(myQuery, handleMyQueryResults, handleError);
}

function handleMyQueryResults(myResultsFeedRoot) {
  if (myResultsFeedRoot.feed.getEntries()[0]) {
    alert("The first search-match entry's title is: " + myResultsFeedRoot.feed.getEntries()[0].getTitle().getText());
  }
  else {
    alert("There are no entries that match the search query.");
  }
}

Updating an item

To update an existing item, first add a line to the end of handleMyQueryResults() to call a new update function:

  updateMyEntry();

Then use the following code. In this example, we're changing the title of the previously retrieved entry (which was contained in the global object named myEntryRoot in an earlier example) from its old text ("Tennis with Darcy") to "Important meeting."

function updateMyEntry() {
  myEntryRoot.entry.getTitle().setText("Important meeting");
  myEntryRoot.entry.updateEntry(handleMyUpdatedEntry, handleError);
}

function handleMyUpdatedEntry(updatedEntryRoot) {
  alert("Entry updated. The new title is: " + updatedEntryRoot.entry.getTitle().getText());
}

As with all of the Calendar methods, the updateEntry() method automatically determines the correct edit URI to use in updating the entry, so you don't have to provide that URI explicitly.

Deleting an item

To delete the updated entry, first add a line to handleMyUpdatedEntry():

 deleteMyEntry(updatedEntryRoot);

Then use the following code:

function deleteMyEntry(updatedEntryRoot) {
  updatedEntryRoot.entry.deleteEntry(handleMyDeletedEntry, handleError);
}

function handleMyDeletedEntry() {
  alert("Entry deleted");
}

Again, the deleteEntry() method automatically determines the correct edit URI to use in deleting the entry.

Note that no entry is returned. If the callback is called, then we know the deletion was successful; if the deletion fails, then deleteEntry() calls handleError() instead of calling handleMyDeletedEntry().

Using ETags

Note: ETags can only be used with services running the Google Data Protocol v2.0.

Introduction

Version 2 of the Google Data JavaScript Client introduces support for ETags. ETags are identifiers that specify a particular version of a particular entry; this is important in two cases:

  • Doing a "conditional retrieval," in which a client requests an entry, and the server sends the entry only if the entry has changed since the last time the client requested it.
  • Ensuring that multiple clients don't inadvertently overwrite one another's changes. The Data APIs do this by making updates and deletes fail if the client specifies an old ETag for the entry.

There are two kinds of ETags: weak and strong. A weak ETag always starts with W/, for example: W/"D08FQn8-eil7ImA9WxZbFEw". Weak ETags are not guaranteed to change when the entry changes, and so HTTP allows them to be used only for conditional retrieval. Strong ETags identify a specific version of a specific entry, and can be used both for conditional retrieval and during updates or deletes to avoid overwriting other clients' changes. Because of this distinction, the client library will not let you send weak ETags with an update or delete request.

ETags can be found in two locations in the server's response:

  • In the ETag HTTP header.
  • In the feed/entry, as the gd:etag attribute.

If a service supports Version 2, each feed and entry object will have a getEtag() method to retrieve the value of the ETag.

The JavaScript client supports two methods for including ETags with a request. The first is the new opt_params object. All get/update/insert functions in Version 2 of the client library have a new opt_params parameter. This object is used to specify optional parameters when making a request. For now, 'etag' is the only supported optional parameter (although other parameters may be introduced in the future). For example, you can add an ETag to a GET request like this:

var opt_params = {};
opt_params['etag'] = 'ETAG GOES HERE';
service.getFeed(uri, successHandler, errorHandler, opt_params);

You can also add an ETag directly to a feed or entry object by calling the setEtag() method on the feed/entry.

You can learn more about ETags from the GData Protocol Reference.

Using ETags to Retrieve Data

ETags can help reduce bandwidth and execution time when retrieving data. An ETag may be included in a GET request with the If-None-Match header:

If-None-Match: ETAG GOES HERE

If the ETag matches the current version of the feed or entry, the server responds with a 304 NOT MODIFIED response and an empty body. Otherwise, the server responds with a 200 OK response and the feed or entry data.

You can use ETags in the JavaScript client by including an 'etag' parameter when making the request:

var etag = feed.getEtag(); // Feed loaded from a previous request
var opt_params = {};
opt_params['etag'] = etag;
service.getFeed(feedUrl, successHandler, errorHandler, opt_params);

Conditional retrievals work with both strong and weak ETags. If the ETag is a match, the error handler will be called with a 304 response:

function successHandler(feedRoot) {
  // 200 response
  // Update UI to display updates
}

function errorHandler(errorObj) {
  if (errorObj.cause.getStatus() == 304) {
    // 304 response, do nothing
  }
  // otherwise the response is some other error
}

Take a look at the Conditional Retrieval Using Blogger sample to see a more practical example of using ETags in the JavaScript client. This sample polls Blogger at 5 second intervals looking for updates to your blog. When there are changes, the sample updates a list of posts.

Using ETags to Update and Delete Data

Using ETags on update/delete requests ensures that multiple clients don't inadvertently overwrite one another's changes. In this case, an ETag is included with the If-Match header:

If-Match: ETAG GOES HERE

If the ETag in the request matches the ETag on the server, the update/delete succeeds. However an ETag mismatch indicates the entry has changed, and the update/delete fails. In this case, the application should request a fresh instance of the data and then try the update/delete again.

In certain cases, you might want to force your changes through, regardless of any other changes to the entry. You can do this by passing a * to the If-Match header:

If-Match: *

Keep in mind that this will override changes made by other clients, so use this carefully.

You can only update/delete an entry with a strong ETag. Specifying a weak ETag will result in an error. To guard against this case, the JavaScript client will not set weak ETags on update and delete requests.

ETags are used in the same way as conditional retrievals:

function updateData(entry, service) {
  var etag = entry.getEtag();
  var opt_params = {};
  opt_params['etag'] = etag; // Or use '*' to force an update.
  service.updateEntry(successHandler, errorHandler, opt_params);
}

function successHandler(response) {
  // Successful update
}

function errorHandler(errorObj) {
  // ERROR - Update failed. Could be due to an ETag mismatch, but check the
  // error message to make sure. An ETag error will be in the format:
  // Mismatch: etags = ["Qnc-fTVSLyp7ImA9WxJbFEsDRAw."], version = [1249675665358000]
}

When making updates, an ETag can be specified in two places:

  1. In the entry itself, using the getEtag() and setEtag() methods.
  2. In the header, using the opt_params object (as demonstrated above).

An entry loaded from a previous GET request will already have an ETag field set. So specifying the same ETag in the opt_params object is redundant. In the case where an ETag is specified both in the entry body and in the opt_params, the ETag in the opt_params will take precedence. This can get a bit confusing, so if you are having issues with conditional updates, be sure to check the ETag in both the entry and the opt_params object.

To make things easier, the google.gdata.Entry classes also have their own updateEntry() and deleteEntry() methods. If the entry class already has an ETag, you don't have to add it to the request; the client library will do this automatically for you. For example:

// entry was loaded from a previous request.  No need to specify
// an ETag in opt_params here, it is added automatically.
entry.deleteEntry(successHandler, errorHandler);

This gives you the benefit of ETags without worrying if you set them correctly. However if you want to force the update using '*', you must always include the opt_params object with 'etag' = '*'.

You can see conditional updates at work in Conditional Updates in Contacts. The sample first creates a test Contact Group where all the data used by this sample will be created. Feel free to delete this Contact Group when you are done using the sample. The sample then loads two iframes with the content from the Contact Group. You can make updates in one iframe and see how it affects updates in the other iframe. Visit the sample for more details on how to use it.

Samples

Reference

For reference information about the classes and methods provided by the client library, see the JavaScript client library API reference (in JSdoc format).

Back to top