Using RestFB with Facebook's Old REST API

As always, you can jump straight to the RestFB Javadoc, or learn by example below.

If you're looking for help or additional examples, please check out the RestFB Google Group.

The below documentation is for the Old REST API component of RestFB.

Keep in mind that it's preferable for new projects to use the default Facebook Graph API component of RestFB, which is documented here. Facebook will be deprecating the Old REST API in the future, and most active development work on both Facebook and RestFB will center around the Graph API.
It's worth noting that there are still two good reasons to use the Old REST API: Legacy code that depends on it, and functionality gaps in the Graph API.

Initialization

// DefaultLegacyFacebookClient is the LegacyFacebookClient implementation
// that ships with RestFB. You can customize it by passing in
// custom JsonMapper and WebRequestor implementations, or simply
// write your own FacebookClient instead for maximum control

// Use this constructor if you'd like to go with legacy authentication (not recommended) 
LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_API_KEY, MY_SECRET_KEY);

// Use this constructor if you'd like to go with the new OAuth token authentication (preferred)
LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN);

Not sure how to get an OAuth access token?

It's really easy with the Facebook Graph API. Sign in to Facebook and navigate to the Graph API documentation. Click on one of the example links that shows Graph API JSON data.
You should see an access_token parameter in your URL bar. That's it! Just copy and paste it into your code.
Example: http://graph.facebook.com/btaylor?access_token=XXXXXXX

Making a Simple Call

We'll get the UID associated with the current Facebook API session key by calling the API method users.getLoggedInUser.

// Last parameter specifies that this API call's result
// should be returned to us as a Long value

Long uid = facebookClient.execute("users.getLoggedInUser", Long.class);

Before we go further, a few words about logging...

RestFB uses java.util.logging to emit information about the data that's sent over the wire to and from Facebook. It's often very helpful to look at the logs so you can make sure that RestFB is sending the data as you expect it to and that Facebook is returning the correct JSON.
Want to use Log4j instead of java.util.logging? No problem! Just download the latest release of slf4j and drop these two JARs onto your classpath and everything should just work:
  • slf4j-api-x.y.jar
  • slf4j-log4j12-x.y.jar

Making a More Complex Call

Here we make the API call fql.query to execute a custom query.

// FQL query which asks Facebook for your friends' names,
// profile picture URLs, and network affiliations

String query =
  "SELECT name, pic_big, affiliations FROM user " +
  "WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=12345)";

// Executes an API call with result mapped to a list of classes we've defined.
// Note that you can pass in an arbitrary number of Parameters - here we
// send along the query as well as the "give me HTTPS URLs" flag

List<User> users =
  facebookClient.executeForList("fql.query", User.class,
    Parameter.with("query", query), Parameter.with("return_ssl_resources", "true"));

1. What Happened Under the Covers

The Facebook API returned JSON in response to our query. You can click here to see it.

2. Turning JSON into Java

Since RestFB doesn't know in advance what kinds of data you're requesting from the API, you need to define your own JavaBean classes that map to API responses. Luckily, this is a simple process - just annotate fields with @Facebook and RestFB will automatically convert Facebook's JSON to real Java objects.

public class User {

  // By default, assumes JSON attribute name is the same as the Java field name  

  @Facebook
  String name;

  // If your Java field name is different than the JSON attribute name,
  // just specify the JSON attribute name
  
  @Facebook("pic_big")
  String pictureUrl;
  
  @Facebook
  List<Affiliation> affiliations;

  public String toString() {
    return String.format("Name: %s\nProfile Image URL: %s\nAffiliations: %s",
      name, pictureUrl, affiliations);
  }
}

public class Affiliation {
  @Facebook
  String name;

  @Facebook
  String type;

  public String toString() {
    return String.format("%s (%s)", name, type);
  }
}

3. Let's See the Results

for (User user : users)
  System.out.println(user + "\n");

And here they are on stdout:

Name: Heather Merlino
Profile Image URL: https://secure-profile.facebook.com/profile6/138/106/n28455_7662.jpg
Affiliations: [Intuit (work)]

Name: Steve Nolan
Profile Image URL: https://secure-profile.facebook.com/v228/344/541/n4057387_1795.jpg
Affiliations: [Cornell (college), Bain & Company (work)]

Name: Third Person
Profile Image URL: https://secure-profile.facebook.com/v2328/314/454/n137387_5491.jpg
Affiliations: []

Making an fql.multiquery Call

Given the unique nature of the fql.multiquery API call, RestFB provides the LegacyFacebookClient.executeMultiquery(Class, MultiqueryParameter, Parameter...) family of methods to easily perform multiquery operations with a minimum of configuration.

To use the multiquery support, simply create a map of named queries and create a class to hold the results. Here's an example:

LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN);

String friendsQuery =
  "SELECT name, pic_big FROM user " +
  "WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=12345)";

String groupsQuery =
  "SELECT gid, name FROM group WHERE gid IN " +
  "(SELECT gid FROM group_member WHERE uid=12345)";

// The key names specified here map to field names in the result object

Map<String, String> queries = new HashMap<String, String>();
queries.put("friends", friendsQuery);
queries.put("groups", groupsQuery);

// You can send along additional parameters if you'd like, too

Results results =
  facebookClient.executeMultiquery(Results.class, MultiqueryParameter.with(queries));

Here's the result class and its supporting classes. By default, results are mapped to @Facebook-annotated fields by the logical query name specified in the map. You may override this behavior by explicitly specifying the value as shown below on the users field.

class Results {
  @Facebook("friends")
  List<User> users;

  @Facebook
  List<Group> groups;
}

// The multiquery support uses the same recursive mapping as the other parts of RestFB.
// It's not shown in this example, but you can create arbitrarily complex object graphs
// of @Facebook-annotated fields to handle complex query results.

class Group {
  @Facebook(value = "gid")
  Long id;

  @Facebook
  String name;
}

class User {
  @Facebook
  String name;

  @Facebook("pic_big")
  String pictureUrl;
}

Making a stream.publish Call with Complex Attachments

The stream.publish API call can accept attachment and privacy (JSON objects) as well as action_links (JSON array) as parameters. The 1.4 release of RestFB supports mapping of Java types to JSON to simplify this use case - you no longer have to manually create a JSON string, as the framework will take care of that for you for Lists, Maps, and your own Javabean types that have fields annotated with the @Facebook annotation.

LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN);

ActionLink category = new ActionLink();
category.href = "http://bit.ly/KYbaN";
category.text = "humor";

Properties properties = new Properties();
properties.category = category;
properties.ratings = "5 stars";

Medium medium = new Medium();
medium.href = "http://bit.ly/187gO1";
medium.src = "http://bit.ly/GaTlC";
medium.type = "image";

Attachment attachment = new Attachment();
attachment.name = "i'm bursting with joy";
attachment.href = "http://bit.ly/187gO1";
attachment.caption = "{*actor*} rated the lolcat 5 stars";
attachment.description = "a funny looking cat";
attachment.properties = properties;
attachment.media = Collections.singletonList(medium);

// Send the request to Facebook.
// We specify the session key to use to make the call, the fact that we're
// expecting a String response, and the attachment (defined above).

String postId = facebookClient.execute("stream.publish", String.class,
  Parameter.with("attachment", attachment));

Here are the supporting classes. You may download the full example and run it locally. Note that you don't have to write your own classes - you can just as easily populate Maps instead for a less strongly-typed solution.

class ActionLink {
  @Facebook
  String text;

  @Facebook
  String href;
}

class Medium {
  @Facebook
  String type;

  @Facebook
  String src;

  @Facebook
  String href;
}

class Properties {
  @Facebook
  ActionLink category;

  @Facebook
  String ratings;
}

class Attachment {
  @Facebook
  String name;

  @Facebook
  String href;

  @Facebook
  String caption;

  @Facebook
  String description;

  @Facebook
  Properties properties;

  @Facebook
  List<Medium> media;
}

Running The Examples

RestFB comes with a few example Java source files that you can run and tweak yourself. Don't forget to put
-Daccess_token=MY_ACCESS_TOKEN in quotes!

$ cd source/examples
$ ant run-legacy-examples "-Daccess_token=MY_ACCESS_TOKEN"

JSON Mapping Rules

Using DefaultJsonMapper, RestFB is able to recursively map JSON fields annotated with @Facebook to the following Java types out of the box:

For example:

public class MyClass {
  @Facebook
  String name;

  @Facebook
  BigDecimal value;

  @Facebook
  List<Integer> numbers;
}

As of RestFB 1.4, Java to JSON mapping is supported by default JsonMapper.toJson(Object object). You may recursively convert primitive wrapper types as specified above, Lists, Maps with String keys, and your own Javabean types by applying the @Facebook annotation to any fields you'd like to include in the conversion.

As-is, DefaultJsonMapper should meet the needs of the vast majority of users. If it doesn't support a feature you need, you can easily subclass it or write your own implementation of JsonMapper instead.

Error Handling

All LegacyFacebookClient methods throw the abstract FacebookException.

These are the FacebookException subclasses that you may catch:

Here's some example code to illustrate the above. Keep in mind that your code doesn't need to handle every single exception the way we're doing here - this is just to demonstrate what's possible.

try {
  Integer result =
    facebookClient.execute("Stream.removeLike", sessionKey,
      Integer.class, Parameter.with("post_id", "12345"));  
} catch (FacebookJsonMappingException e) {
  // Looks like this API method didn't really return an Integer
} catch (FacebookNetworkException e) {
  // Looks like an error occurred at the network level
  System.out.println("API returned HTTP status code " + e.getHttpStatusCode());
} catch (FacebookResponseStatusException e) {
  // Facebook API returned a specific error
  if (e.getErrorCode() == 200)
    System.out.println("Permission denied!");
} catch (FacebookException e) {
  // This is the catchall handler for any kind of Facebook exception
}

Extensibility and Unit Testing

In addition to LegacyFacebookClient, RestFB provides default implementations for WebRequestor and JsonMapper, two components that DefaultFacebookClient depends on to do its work.

These dependencies are designed to allow for straightforward subclassing (if you only want to replace a little bit of functionality) and simple custom implementations (if you require full control).

This comes in handy when unit testing - for example, you can write your own WebRequestor implementation that simulates a Facebook API endpoint response. You can drop in custom data designed to exercise your application's Facebook integration or simulate error conditions to make sure you're handling them properly.

Here's a trivial example which shows one way you might implement this:

LegacyFacebookClient facebookClient =
  new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN,
  
    // A one-off DefaultWebRequestor for testing that returns a hardcoded JSON
    // list of numbers instead of hitting the Facebook API endpoint URL

    new DefaultWebRequestor() {
      @Override
      public Response executePost(String url, String parameters) throws IOException {
        return new Response(HttpURLConnection.HTTP_OK, "[123,456,789]");
      }
    }, new DefaultJsonMapper());

// Make an API request using the mocked WebRequestor

List<Integer> numbers = facebookClient.executeForList("ignored", Integer.class); 

// Make sure we got what we were expecting

assert numbers.size() == 3;
assert numbers.get(1) == 456;