Requesting Social Data using JavaScript (v0.9)

Data requests are a fundamental aspect of the OpenSocial API. In this article, I introduce the types of requests available in OpenSocial, summarize their expected arguments, and provide examples that demonstrate how each is used. So let's get to it!

Data Requests overview

The OpenSocial API defines six classes of data requests:

  • Fetch person requests return information for a single individual. The person to be fetched can be identified using an OpenSocial ID or using one of two constants provided by the API.
  • Fetch people requests return information for a group of individuals. Constants are provided for retrieving the friends of the current user but you can also pass an array of OpenSocial IDs to retrieve specific persons.
  • Update app data requests store persistent, user-specific data (e.g. user preferences, high scores, etc.) that can be fetched at a later time.
  • Fetch app data requests retrieve app data stored previously.
  • Remove app data requests clear app data stored previously.
  • Fetch activities requests return a collection of activity objects generated previously for the indicated person or group. Essentially, this allows you to display an application-specific activity stream tailored for the current user, profile owner, etc.

Although each of these requests are unique, they share a similar syntax in OpenSocial. In the sections below, each is explored in greater detail and small samples are provided to help you get started.

The DataRequest object

The best way to kick off this article is with a quick overview of the opensocial.DataRequest class, which, in low-level terms, represents an HTTP request. It's imperative to understand that an {{opensocial.DataRequest{{ object is not a request for OpenSocial data—instead, one or more data requests (e.g. fetch person and fetch people requests) can be "attached" to the object. In this way, you can retrieve all of the information you need by sending out a single HTTP request instead of firing multiple asynchronous requests and handling their responses individually.

To create a new DataRequest object, use the newDataRequest() method under the opensocial namespace:

var req = opensocial.newDataRequest();

Individual requests are added to a DataRequest object by passing them into its add() method. A key is typically passed as well which acts as a label for the request—because the DataRequest object can aggregate multiple data requests, a unique label for each is necessary in order to be able to reference the retrieved data in the response handler.

req.add(''request'', ''key'');

Finally, after adding all of the requests to the DataRequest object, you must call its send() method to actually submit the request. The only argument to send() is an optional callback handler, a JavaScript function that is executed when the requested data is available.

req.send(fetchHandler);

This concludes the broad, high-level overview of OpenSocial data requests. The sections below discuss the types of requests in more detail.

A note about OpenSocial identifiers

The OpenSocial API defines two individual roles: owner and viewer. The owner of an application represents the individual whose profile hosts it while the viewer represents the individual using it. The viewer and the owner may or may not represent the same person; for example, if you are using an application that you have installed on your own profile, you are both the owner and viewer. Conversely, the owner and viewer are different if a user interacts with an application installed on a friend's profile.

These roles are defined here because all six of the data requests covered below require an id or idSpec argument to identify the individual or group being "targeted" by the request, and these two roles are key to specifying this identification.

Three of the six types of data requests retrieves or updates data for a single individual. For these, two constants, opensocial.IdSpec.PersonId.OWNER and opensocial.IdSpec.PersonId.VIEWER, are provided. Passing the string literals "OWNER" and "VIEWER" or an OpenSocial ID string (for accessing information for an arbitrary user) are also valid.

The second set of requests can access data for a single person or a group. For these, an opensocial.IdSpec object is required. You can create this object by calling the opensocial.newIdSpec method and passing in an object identifying the targeted user or users. This object is keyed on two objects: opensocial.IdSpec.Field.USER_ID and opensocial.IdSpec.Field.GROUP_ID. The USER_ID field can be either be the OWNER and VIEWER constants discussed above, an OpenSocial ID string, or an array of OpenSocial ID strings if you're looking to access information for two or more specific users. Meanwhile, the GROUP_ID field can contain either "FRIENDS" or "SELF".

For example, to target the current user's friends, you can use the following snippet to create the IdSpec object:

var idspec_params = {};
idspec_params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.OWNER;
idspec_params[opensocial.IdSpec.Field.GROUP_ID] = 'FRIENDS';

var idspec = opensocial.newIdSpec(idspec_params);

Optionally, you can use the following shorthand to declare this opensocial.IdSpec object in a single line:

var idspec = opensocial.newIdSpec({'userId':'OWNER', 'groupId':'FRIENDS'});

Fetching a single person

Fetch person requests are created by calling your opensocial.DataRequest object's newFetchPersonRequest() method, passing in an ID string or constant and, optionally, an object specifying pertinent parameters such as the extended profile fields to return with the person. The ID argument can be the OWNER or VIEWER constants discussed above a single OpenSocial ID string. After the request is sent, the API should return an opensocial.Person object representing the specified individual.

Here is the full code listing for a simple request for the application's owner. Notice that the 'owner' key has been passed with the fetch person request so that the retrieved profile information can be accessed in the callback function as described above. Also note that the function fetchPersonHandler() will be executed automatically when the response is ready.

var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), 'owner');
req.send(fetchPersonHandler);

That's it! A quick example callback routine is provided below that displays the name of the fetched person in an alert dialog. The resp argument is an opensocial.DataResponse object passed to the function by the API.

function fetchPersonHandler(resp) {

  var ownerResp = resp.get('owner');  // use the key passed with the request to "get" the appropriate data


  if (!ownerResp.hadError()) { // always verify whether your request was successful

    var owner = ownerResp.getData(); // store the {{opensocial.Person}} object representing the app's owner
    alert(owner.getDisplayName()); // retrieve the name using the {{opensocial.Person}} object's getDisplayName() method
  }

};

Enhancing your fetch person request

By default, only basic profile information (e.g. name, profile picture, etc.) is returned with the simple data request used above. However, additional fields such as gender, location, interests, and so forth can also be requested. You can specify that you want these fields returned by passing a special object into your fetch person request. The desired fields must be collected in an array that is assigned to the object property named using the opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS constant:

Parameter Name

Accepted Values

Description

PROFILE_DETAILS

JavaScript array of objects under the opensocial.Person.Field namespace

Specifies extended profile fields (e.g. gender, favorite books, etc.) to return for the requested person. (Note that extended profile fields may only be available if the fetched person has the app installed).

The basic sample above can be extended by specifying that the owner's gender and favorite books should be returned along with his name and other basic information. This is done by passing in a special object that includes an array of preferred opensocial.Person.Field objects after the id argument:

var params = {};

params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [opensocial.Person.Field.BOOKS, opensocial.Person.Field.GENDER];

var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER, *params*), 'owner');
req.send(fetchPersonHandler);

Now you should be able to output the owner's favorite books in the callback routine:

function fetchPersonHandler(resp) {
  var ownerResp = resp.get('owner'); 
  
  if (!ownerResp.hadError()) {
    var owner = ownerResp.getData();
    
    // Use the opensocial.Person class' getField() method to access specially requested fields
    alert(owner.getField(opensocial.Person.Field.BOOKS));
  }

};

Fetching a group of people

This section introduces the fetch people request, which is commonly used to retrieve the friends of the current viewer or app owner but can also retrieve specific individuals if their OpenSocial IDs are known. As you'll see in the examples below, its syntax is largely similar to that of the fetch person request covered above with one exception: the identifier must be an opensocial.IdSpec object instead of a string or string constant. This similarity works to your advantage—once you master one or two types, you're well on your way to mastering them all.

Fetch people requests are created by calling your opensocial.DataRequest object's newFetchPeopleRequest method, passing in an opensocial.IdSpec argument and, optionally, an object specifying pertinent parameters. In response, the API returns an opensocial.Collection object which can be iterated through to access the opensocial.Person objects requested.

The following snippet shows a fetch people request in action using the IdSpec shorthand identified above:

var req = opensocial.newDataRequest();
var idspec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'FRIENDS'});

req.add(req.newFetchPeopleRequest(idspec), 'viewerFriends');
req.send(fetchPeopleHandler);

The above request retrieves the friends of the application's current user and includes a key ('viewerFriends') so this group can be accessed in fetchPeopleHandler, the specified callback.

Enhancing your fetch people request

Like fetch person requests, fetch people requests can include a special object that specifies a set of additional parameters to customize the data set returned, all living under the opensocial.DataRequest.PeopleRequestFields namespace.

Parameter Name

Accepted Values

Description

ALL

opensocial.DataRequest.FilterType.ALL (default)

 

FILTER

opensocial.DataRequest.FilterType.HAS_APP

opensocial.DataRequest.FilterType.TOP_FRIENDS - Specifies which persons are returned; since extended profile information is only available for those individuals who also have the app installed, you may want to limit the set of returned objects based on this criteria.

 

FIRST

JavaScript number

Specifies the starting index to use for paging purposes; objects are returned beginning from this index.

MAX

JavaScript number

Specifies the number of objects to return (defaults to 20); for performance reasons, it is often preferable to fetch a limited number of objects in a single request and MAX is used to specify the limit.

PROFILE_DETAIL

JavaScript array of objects under the opensocial.Person.Field namespace

Specifies extended profile fields (e.g. gender, favorite books, etc.) to return for the requested individuals. (Note that extended profile fields may only be available for those who have the app installed).

 

opensocial.DataRequest.SortOrder.TOP_FRIENDS (default)

 

SORT_ORDER

opensocial.DataRequest.SortOrder.NAME

Specifies the order in which fetched individuals are returned, defaulting to TOP_FRIENDS. If set to NAME, objects are returned in alphabetical order by name field.

 

The following snippet demonstrates the FILTER, MAX, and SORT_ORDER parameters by retrieving an alphabetically-sorted list of no more than 50 of the user's friends, all of whom have the application installed:

var params = {};
params[opensocial.DataRequest.PeopleRequestFields.MAX] = 50;
params[opensocial.DataRequest.PeopleRequestFields.FILTER] = opensocial.DataRequest.FilterType.HAS_APP;
params[opensocial.DataRequest.PeopleRequestFields.SORT_ORDER] = opensocial.DataRequest.SortOrder.NAME;

var req = opensocial.newDataRequest();
var idspec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'FRIENDS'});

req.add(req.newFetchPeopleRequest(idspec, *params*), 'viewerFriends');
req.send(fetchPeopleHandler);

Now that your request is polished, it's time to implement the callback function. The following routine iterates through the returned collection and displays the name of all returned individuals in an alert dialog:

function fetchPeopleHandler(resp) {

  var viewerFriendsResp = resp.get('viewerFriends'); // use the key passed with the request to "get" the appropriate data


  if (!viewerFriendsResp.hadError()) { // always verify whether your request was successful

    var viewerFriends = viewerFriendsResp.getData();


    viewerFriends.each( // {{each}} method is used to iterate through every object in collection

      function(person) { // anonymous function is called once for every object in collection
        alert(person.getDisplayName());
      }
    );
  }

};

Updating a person's persistent app data

The OpenSocial API specification includes support for storing (and retrieving) persistent, user-specific information using client-side data requests. To store new information, create an update app data request by calling your opensocial.DataRequest object's newUpdatePersonAppDataRequest method which expects three arguments: an ID string identifying the user to associate with the stored data, a label for this data (to be used during retrieval), and the data itself which must be a formatted as a string.

Note that each container can set its own constraints on which profiles your application can write data to. In orkut, for example, only viewer data may be written—the system will throw an error if your application tries to update app data for any other user, so keep this in mind when designing your application.

The sample below stores the current time for the current user under the 'time' key:

var currentTime = new Date().getTime().toString();

var req = opensocial.newDataRequest();
req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'time', currentTime));
req.send();

Notice that a callback was not passed with send() as it was in previous examples. A callback is an optional argument that comes in handy when fetching data. Since data is only being updated data here, no callback is defined. For real-world apps, you should consider including a callback anyway to check whether an error is generated, especially since some OpenSocial containers do not support the persistence layer.

Two final notes of interest with regards to persistence:

  • You can overwrite data to your heart's content—if you try to update a person's app data using a previously-defined key, the new value will simply replace the existing value.
  • If you want to store more than one piece of data for a given string, you may consider serializing your data into a JSON-based string. While this technique is outside the scope of the tutorial, you can check out the gadgets.json library to find a couple of available functions that simplify converting data to and from JSON strings.

Fetching app data for a person or group

Fetch app data requests, created using the opensocial.DataRequest method newFetchPersonAppDataRequest, are used to retrieve information stored in the persistence layer. Like the fetch people request covered earlier, newFetchPersonAppDataRequest() expects an opensocial.IdSpec object to identify the individual or group to fetch data for. You must also provide a data key or array of keys indicating which data to retrieve. (Alternatively, you can specify '*' to retrieve all persisted app data for the indicated person or group.) Note that while containers may limit which profiles your application has update app data privileges for (such as orkut), these restrictions do not necessarily apply when fetching. In orkut, for example, your application can only write to the viewer's data store but can fetch app data for any user who has the application installed on their own profile.

The following example creates a data request to retrieve the time stored in the previous sample:

var req = opensocial.newDataRequest();
var idspec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'SELF'});

req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
req.add(req.newFetchPersonAppDataRequest(idspec, 'time'), 'data');
req.send(fetchAppDataHandler);

A callback function is provided since data is being fetched. The sample handler below displays the retrieved value in an alert dialog:

function fetchAppDataHandler(resp) {

  var viewerResp = resp.get('viewer');
  var dataResp = resp.get('data');


  if (!viewerResp.hadError() && !dataResp.hadError()) {

    var viewer = viewerResp.getData();
    var data = dataResp.getData();


    var viewerData = data[viewer.getId()];


    if (viewerData) {
      alert(viewerData['time']);
    }
  }

};

The returned app data is contained in a JavaScript map (associative array) indexed by data key, which is in turn contained within another map indexed by OpenSocial ID. So in order to access the viewer's data, you will need the viewer's ID, which is why the viewer is also fetched above. This is necessary since app data can be fetched for a several individuals at once by using an appropriate opensocial.IdSpec object.

Fetch app data requests can also accept an object specifying additional parameters. The table below summarizes these:

Parameter Name

Accepted Values

Description

ESCAPE_TYPE & opensocial.EscapeType.HTML_ESCAPE opensocial.EscapeType.NONE

Specifies how the retrieved data should be escaped by the container before its returned to the application. In general, you should always use HTML_ESCAPE if you plan to output the retrieved data to the screen since not doing so may corrupt the display or even expose a security vulnerability.

The snippet below is a slight revision to the earlier request indicating that the retrieved data should be properly escaped for display in the browser:

var params = {};
params[opensocial.DataRequest.DataRequestFields.ESCAPE_TYPE] = opensocial.EscapeType.HTML_ESCAPE;

var req = opensocial.newDataRequest();
var idspec = opensocial.newIdSpec({'userId':'VIEWER', 'groupId':'SELF'});

req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
req.add(req.newFetchPersonAppDataRequest(idspec, 'time', params), 'data');
req.send(fetchAppDataHandler);

Removing a person's app data

New to version 0.8 of the OpenSocial specification, the remove app data request allows developers to programatically remove app data previously stored. The method that does this is opensocial.newRemovePersonAppDataRequest which accepts an ID string identifying the user whose data should be cleared along with a key name or array of key names to clear. This request does not return any data so there is no need for a callback function.

The following snippet removes the data stored above:

var req = opensocial.newDataRequest();
req.add(req.newRemovePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'time'));
req.send();

Note: Instead of passing in a key or array of keys, you can specify '*' to clear all keys.

Fetching the activities of a person or group

The last data request covered in this article is used to fetch the activities that have been posted by that application for a given person or group, effectively enabling you to display an activity stream tailored for the current user, profile owner, etc. directly in your app. This request is made using the opensocial.newFetchActivitiesRequest method and returns a Collection of opensocial.Activity objects. This method requires an opensocial.IdSpec argument indicating which person or group to fetch activities for. (The specification also allows an object to be passed specifying additional parameters although no additional options are available at present.)

The block below demonstrates how to issue a request for all activites posted on behalf of the application's owner:

var req = opensocial.newDataRequest();
var idspec = opensocial.newIdSpec({'userId':'OWNER', 'groupId':'SELF'});

req.add(req.newFetchActivitiesRequest(idspec), 'ownerActivities');
req.send(fetchActivitiesHandler);

Conclusion

The OpenSocial API provides a robust, extensible interface for retrieving and using social data in your applications, and fetching both person and app data are fundamental to using the API effectively. This article hopefully helped you better understand how these requests are formed and sent and that you picked up a tidbit of knowledge you can use in your own OpenSocial applications. Have fun!

Resources