Building and personalizing social applications with new OpenSocial JavaScript APIs

The majority of OpenSocial networks implement (or support) version 0.8 or version 0.9 of the specification. While version 1.0 is the upcoming release, version 0.9 will be the main focus of this post as it is the current standard employed by the Yahoo! Application Platform. We will specifically explore the lightweight JavaScript APIs introduced in OpenSocial 0.9, as well as some of the helper methods available.


What Is OpenSocial

OpenSocial is a set of common APIs for working with data from various social networks. The concept behind OpenSocial is to develop once and distribute broadly, meaning that if the OpenSocial specification is used to build out an application, it can be easily and quickly ported to another platform. This concept is important, because when building a social application, you want to reach the largest audience you can. Portability, along with the vast amount of user data that can be pulled, makes OpenSocial an ideal tool for quick and scalable development of social applications.

Many networks and companies implement the OpenSocial specification, such as the Yahoo! Application Platform, MySpace, Orkut, and a large number of others. The OpenSocial site offers a full list of networks implementing the OpenSocial specification.


A Few Core Concepts

Before diving further into making OpenSocial JavaScript requests, there are a few core concepts of what we will cover in this spec as well as request methods that should be reviewed.

Lightweight JavaScript APIs

With the implementation of OpenSocial 0.9 comes a new set of lightweight JavaScript APIs to help developers by providing simple JSON input/output through simple methods when making data requests to the REST APIs. This new feature significantly reduces the amount of code required to make requests and should reduce ramp-up time for developers. These APIs use a new namespace (osapi) so that the requests will be nicely sandboxed from your older code base.

Batching Requests

One of the important aspects to be aware of when making OpenSocial JavaScript requests is request batching. Instead of having to make multiple requests to capture profile information, connection details or updates, a user can tie all of those data fetches into a single AJAX request instead of three. This brings quite a performance boost to your application, for obvious reasons.

Additionally, batching requests will ensure that all data is obtained from the request prior to the callback being executed. In Listing 1, we set up a request to capture profile details for the owner of the application (labeled as ownerData) and then another request for the friends of the owner (labeled as ownerFriends).

var batch = osapi.newBatch().
add("ownerData", osapi.people.getOwner({fields: ['name']})).
add('ownerFriends', osapi.people.get({userId: '@owner', groupId: '@friends'}));

batch.execute(function(result){
var ownerName = result.ownerData.name.formatted;
});

Listing 1 - Batching Data Requests

We’ll explore these requests in more detail in the following sections.


Capture User Profile Data to Personalize Your Application

Due to the rise of many social networks over the years, one realization about users has become blatantly obvious: the vast majority of people want everyone to know as much about them as they can cram onto their profile. While this is a concern from a privacy and "good sense" perspective, this wealth of knowledge provides the application developer with an extremely large warehouse of data that can be used to personalize applications.

This can be anything, from demographically targeting ads, all the way to gender personalization. With all of this information readily available for use, the benefit here is quite clear.

Let’s look at this from another angle. One thing that many users have become intolerant of is having to build multiple profiles for every application or social network they use. If you’re using an application on a popular social network like Facebook or MySpace, why would you want to have to type out the exact same information in that application that you’ve already spent a painstaking amount of time entering into your profile? The simple fact is you don’t have to — and that’s why capturing and using the profile information of a user to pre-customize their experience is vital.

This task can be accomplished quickly and easily using the getViewer or getOwner methods within OpenSocial (Listing 2).

//opensocial person data request
osapi.people.getViewer({fields: ['name']}).execute(function(result){
if (!result.error){
var name = result.name.formatted;
}
});

Listing 2 - Capturing User Profile Information

To create a request to get details about a person, you need to take a few steps (all chained nicely together though). Breaking down the request into its individual parts, we have three distinct sections.

  • osapi.people.getViewer(...) is the method which defines that we want to capture profile information from the viewer. The parameter that we pass into this method is a JSON structure, which defines the fields that we want to return from the profile. In this case, we want to capture the name of the viewer.
  • execute(...) will initialize the viewer data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the viewer data request completes and will contain the person object returned (result parameter). From this result we can use standard dot notation to get at the data we requested.

Every network implements its own list of supported OpenSocial.Person fields, depending on what’s included in their profile system. The full list of OpenSocial 0.9 Person fields is described in the wiki.


Utilize User Connections to Build Your Social Graph

The connections a user creates generally builds out a social graph, which can be used to increase your user base and drive up activity. Connections are a two-way reciprocated link between two users, and when present, means that you are usually able to leverage off profile details of a wider range of people. These concepts work equally well in a follower model such as Twitter; you’re looking for a means of using a physical connection (like a friendship or work tie) to your advantage.

Many of us who are present on any of the popular social networks have either participated in (or at least received) annoying status updates from one of the virally popular games such as Mafia Wars, Vampires, or Zombies. Peering into the innards of Mafia Wars, I can see that one of the reasons it has become so virally successful is because of the vast relevant social graph that they’ve built up in their game.

The important word here is relevant — something that many networks struggle with at times. Most users of a social platform add many more people other than friends and family — coworkers, the postman, the cousin of the neighbor you say hi to while getting the paper in the morning — and these connections aren’t really what we can call relevant to the user.

This means that, for the social container itself, the link between non-relevant users has far less value than one where the users know each other or interact with each other on a regular basis. This same concept is true for your application: When you build the links between the people who use your application, they need to have a way to interact with each other on a regular basis to be of any use to you.

Mafia Wars does this by adding your application connections into your “mob,” effectively making them interact with each other in some form whenever they play the game.

Much like our previous person request, we can make a simple get request to capture owner friends (Listing 3).

//get owner friends
osapi.people.get({userId: '@owner', groupId: '@friends', count: 10}).execute(function(result){
if (!result.error){
var friends = result.list;
var html = '';
for (var i = 0; i < friends.length; i++){
html += friends[i].name.formatted + '';
}
}
});

Listing 3 - Capturing User Connections

Again, this request can be split up into three individual sections:

  • osapi.people.get(...) is a standard get method that can be used to capture any people data. In this case, we denote within the JSON passed into this function that we would like to capture information from the "owner" of the application where the groupId is "friends". These are simple strings defining that we would like to capture the owner's friends.
  • execute(...) will initialize the owner friend data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the owner friend data request completes and contains all friend person objects for the owner (friends parameter). From this result, we can get the number of friends returned using .totalResults or loop through the list of person objects for each friend.


Promote and Customize Your Application Using Updates

A powerful tool for social application developers is the ability to send updates to the stream of a user. The update stream is the central news area for the user and their connections.

Getting Updates

Earlier I mentioned that users will go to great lengths to add in every detail about themselves and their lives into their profiles. What I didn’t mention is that those details might not always be fully accurate, or they may be out of date.

The defining truth in any social network is the update stream of the user. When the user sends messages, who they’re contacting, what they’re doing, and what applications they’re using will all become accessible through this stream. This type of request will follow the same syntax as our previous requests but will use the activities API rather than the person API (Listing 4).

//capture viewer activities
osapi.activities.get({userId: '@viewer', count: 20}).execute(function(result){
if (!result.error){
var activities = result.list;
var html = '';

for (var i = 0; i < activities.length; i++){
html += 'Activity Title: ' + activities[i].title +
'Activity URL: ' + activities[i].url;
}
}
});

Listing 4 - Capturing User Activities

Within this request we are making a call to osapi.activities.get(...), denoting that we wish to return someone's activity stream. The JSON object provided as the parameter to this request denotes that we want the activities for a userid that matches that of the current application viewer and we would also like to return only 20 activities.

Once this request completes, we can parse through each of the activities and use them however we'd like.

For a full list of activity fields defined within the spec, see Opensocial.Activity.

Setting Updates

One of the best methods that a developer can use to encourage user growth in an application is to promote the application through the user update stream. Sadly, networks are generally overly saturated with applications, and they sometimes place applications in undesirable locations.

The update stream is one of the few prime-time outlets to users that developers still have access to. When a developer taps into this by setting updates that draw in the users attention, they generally see a larger number of users coming to their application. When implementing this in your code, the syntax will look familiar to our previous GET request (Listing 5).

//insert new activity for the current viewer
osapi.activities.create({
userId: '@viewer',
activity: {
title: 'This is my activity',
url: 'https://www.yahoo.com/'
}
}).execute();

Listing 5 - Setting User Updates Setup

To accomplish the update insert, we make a request to osapi.activities.create(...). As with our other requests, we will pass in a JSON payload defining what we'd like to insert. In the preceding example, we set the userId to viewer to denote that we would like to insert the update for the current application user. Within the activity definition, we create an object which contains the content of the activity. Here we define the title and URL that the title should link to.

There are numerous additional fields available for developers to use in order to further customize the update.


The Gadgets API Tool Set

The gadgets API specification within OpenSocial contains a number of helpful utilities to make your development life easier. When building out applications in a container that has heavy restrictions on front-end code, these helper functions will be a lifesaver. Even if the restrictions aren’t there, not having to build out this functionality yourself makes development quicker — no need to reinvent the wheel.

AJAX Requests with gadgets.io

One of the helpful utilities under gadgets.io is the makeRequest function for making AJAX requests. When creating social applications, it’s important that the user experience should be as seamless as possible. In this regard, running data requests in the background is an important implementation tactic.

var params = {};
var url = 'http://www.mysite.com/myfile.php';
var callback = callbackFunc;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.
io.ContentType.TEXT;
params[gadgets.io.RequestParameters.METHOD] = gadgets.
io.MethodType.GET;
gadgets.io.makeRequest(url, callback, params);
function callbackFunc(response){
if (response.text){
//use data returned from server
}
}

Listing 6 - Making AJAX Requests

In the makeRequest method, we define our parameter list to be the type and method of data request that we’re making. We set the content type as “TEXT” and the method as “GET”. The makeRequest call takes in three parameters; the URL to be called, the callback function to call once completed, and the optional parameter list defining what the content / method types are. When the callback is instantiated, we can pull our response object from response.text.

The complete gadgets.io spec can be found on OpenSocial gadgets.io.

Localization Support with gadgets.Prefs

Another helpful utility within the gadgets spec provides the ability to pull out user localization information for your application using gadgets.Prefs. When building applications, chances are that your app will be seen throughout numerous countries, with numerous languages. Being aware of these details will allow you to better gear your application towards the needs of the viewer.

Using gadgets.Prefs, we can pull out the country and language of preference for the user. These two values will go a long way towards customizing your application for your user base (Listing 7).

var prefs = new gadgets.Prefs();
var country = prefs.getCountry();
var language = prefs.getLang();

Listing 7 - Capturing User Localization Information

The complete gadgets.Prefs spec can be found on OpenSocial gadgets.Prefs.


Summary

OpenSocial seeks to bridge a large gap, providing common methods for accessing social data on a wide range of networks, allowing developers to build once and distribute broadly. This concept allows a developer to construct an application and deploy it across many supporting networks, providing maximum impact and the most views for a minimal effort.