RestFB

RestFB is a simple and flexible Facebook Graph API and Old REST API client written in Java.

Latest News

Version 1.5.3 of RestFB is now available (released July 21, 2010).
Version 1.6 is under active development.

Download

The latest version of the library is available via Google Code. You may view the changelog, or download it here.
The project zip contains example code which can help you get up and running quickly.

Motivations

Design goals:

Non-goals:

If RestFB doesn't meet your needs, there are some alternative clients available.
BatchFB, a sister project, is a good lower-level client especially if you're concerned with batching and performance.

How To Use It

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.

It's helpful to see exactly what's going over the wire to verify that RestFB is taking your input and sending it to Facebook exactly as you expect. Feel free to use this example log4j.xml file in your own project to see full POST request and response data logged to standard output.

The below documentation is for the Facebook Graph API component of RestFB.

If you'd like to code against the Old REST Facebook API, though, RestFB has you covered. Just use LegacyFacebookClient, which is documented here. It even supports the new OAuth2 authentication scheme!

Initialization

// DefaultFacebookClient is the FacebookClient 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.

FacebookClient facebookClient = new DefaultFacebookClient(MY_ACCESS_TOKEN);

// As of 1.5.2, it's also possible to create a client that can only access
// publicly-visible data - no access token required. 
// Note that many of the examples below will not work unless you supply an access token! 

FacebookClient publicOnlyFacebookClient = new DefaultFacebookClient();

Not sure how to get an OAuth2 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

Fetching Single Objects

// For all API calls, you need to tell RestFB how to turn the JSON
// returned by Facebook into Java objects.  In this case, the data
// we get back should be mapped to the User and Page types, respectively.
// You can write your own types too!

User user = facebookClient.fetchObject("me", User.class);
Page page = facebookClient.fetchObject("cocacola", Page.class);

out.println("User name: " + user.getName());
out.println("Page fan count: " + page.getFanCount());

Fetching Connections

Connection<User> myFriends = facebookClient.fetchConnection("me/friends", User.class);
Connection<Post> myFeed = facebookClient.fetchConnection("me/feed", Post.class);

out.println("Count of my friends: " + myFriends.getData().size());
out.println("First item in my feed: " + myFeed.getData().get(0));

// Connections support paging

if(myFeed.hasNext()) {
  Connection<User> myFeedPage2 = facebookClient.fetchConnection("me/feed", Post.class,
    Parameter.with("offset", myFeed.getData().size()));
  ...
}

Selecting Specific Fields (see Graph API documentation)

User user = facebookClient.fetchObject("me", User.class,
  Parameter.with("fields", "id, name, picture"));

out.println("User picture: " + user.getPicture());

Fetching Multiple Objects in One Call (see Graph API documentation)

FetchObjectsResults fetchObjectsResults =
  facebookClient.fetchObjects(Arrays.asList("me", "cocacola"), FetchObjectsResults.class);

out.println("User name: " + fetchObjectsResults.me.getName());
out.println("Page fan count: " + fetchObjectsResults.page.getFanCount());

...

// Holds results from a "fetchObjects" call.
// You need to write this class yourself!

public class FetchObjectsResults {
  @Facebook
  User me;

  // If the Facebook property name doesn't match
  // the Java field name, specify it with the "value" annotation property.

  @Facebook(value = "cocacola")
  Page page;
}

Executing FQL Queries (see FQL documentation)

String query = "SELECT uid, name FROM user WHERE uid=220439 or uid=7901103";
List<QueryResult> users = facebookClient.executeQuery(query, QueryResult.class);

out.println("Users: " + users);

...

// Holds results from an "executeQuery" call.
// You need to write this class yourself!

public class QueryResult {
  @Facebook
  String uid;

  @Facebook
  String name;

  @Override
  public String toString() {
    return String.format("%s - %s", uid, name);	
  }
}

Executing Multiple FQL Queries in One Call (see FQL documentation)

Map<String, String> queries = new HashMap<String, String>();
queries.put("users", "SELECT name FROM user WHERE uid=220439 OR uid=7901103");
queries.put("likers", "SELECT user_id FROM like WHERE object_id=122788341354");

MultiqueryResults multiqueryResults =
  facebookClient.executeMultiquery(queries, MultiqueryResults.class);

out.println("User count: " + multiqueryResults.users.size());
out.println("People who liked: " + multiqueryResults.likers.size());

...

// Holds results from an "executeMultiquery" call.
// You need to write this class yourself!

public class MultiqueryResults {
  @Facebook(contains = User.class)
  List<User> users;

  @Facebook(contains = String.class)
  List<String> likers;
}

Searching (see Graph API documentation)

// Searching is just a special case of fetching Connections -
// all you have to do is pass along a few extra parameters.

Connection<Post> publicSearch =
  facebookClient.fetchConnection("search", Post.class,
    Parameter.with("q", "watermelon"), Parameter.with("type", "post"));

Connection<User> targetedSearch =
  facebookClient.fetchConnection("me/home", User.class,
    Parameter.with("q", "Mark"), Parameter.with("type", "user"));

out.println("Public search: " + publicSearch.getData().get(0).getMessage());
out.println("Posts on my wall by friends named Mark: " + targetedSearch.getData().size());

Metadata/Introspection (see Graph API documentation)

// You can specify metadata=1 for many calls, not just this one.
// See the Facebook Graph API documentation for more details. 

User userWithMetadata =
  facebookClient.fetchObject("me", User.class, Parameter.with("metadata", 1));

out.println("User metadata: has albums? "
  + userWithMetadata.getMetadata().getConnections().hasAlbums());

Passing Parameters (see Graph API documentation)

// You can pass along any parameters you'd like to the Facebook endpoint.

Date oneWeekAgo = new Date(System.currentTimeMillis() / 1000L - 60L * 60L * 24L * 7L);

Connection<Post> filteredFeed = facebookClient.fetchConnection("me/feed", Post.class,
  Parameter.with("limit", 3), Parameter.with("until", "yesterday"),
    Parameter.with("since", oneWeekAgo));

out.println("Filtered feed count: " + filteredFeed.getData().size());

A note about the Publish and Delete examples below

In order to use these, you'll need to create a Facebook Application and then request an OAuth2 access token with the proper permissions, e.g. publish_stream,offline_access,create_event.
The Facebook Graph API authorization documentation explains how to do this in detail.
If you're in a hurry, though, here's a quick example:
  • Create a Facebook Application
  • Request https://graph.facebook.com/oauth/authorize?client_id=MY_API_KEY& redirect_uri=http://www.facebook.com/connect/login_success.html& scope=publish_stream,offline_access,create_event
  • Facebook will redirect you to http://www.facebook.com/connect/login_success.html? code=MY_VERIFICATION_CODE
  • Request https://graph.facebook.com/oauth/access_token?client_id=MY_API_KEY& redirect_uri=http://www.facebook.com/connect/login_success.html& client_secret=MY_API_SECRET&code=MY_VERIFICATION_CODE
  • Facebook will respond with access_token=MY_ACCESS_TOKEN

Publishing (see Graph API documentation)

// Publishing a simple message.
// FacebookType represents any Facebook Graph Object that has an ID property.

FacebookType publishMessageResponse =
  facebookClient.publish("me/feed", FacebookType.class,
    Parameter.with("message", "RestFB test"));

out.println("Published message ID: " + publishMessageResponse.getId());

// Publishing an event

Long tomorrow = System.currentTimeMillis() / 1000L + 60L * 60L * 24L;
Long twoDaysFromNow = System.currentTimeMillis() / 1000L + 60L * 60L * 48L;

FacebookType publishEventResponse = facebookClient.publish("me/events", FacebookType.class,
  Parameter.with("name", "Party"), Parameter.with("start_time", tomorrow),
    Parameter.with("end_time", twoDaysFromNow));

out.println("Published event ID: " + publishEventResponse.getId());

// Publishing an image to a photo album is easy!
// Just specify the image you'd like to upload and RestFB will handle it from there.

FacebookType publishPhotoResponse = facebookClient.publish("me/photos", FacebookType.class,
  getClass().getResourceAsStream("/cat.png"), Parameter.with("message", "Test cat"));

out.println("Published photo ID: " + publishPhotoResponse.getId());

Deleting (see Graph API documentation)

Boolean deleted = facebookClient.deleteObject("some object ID");
out.println("Deleted object? " + deleted);

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-reader-examples "-Daccess_token=MY_ACCESS_TOKEN"
$ ant run-publisher-examples "-Daccess_token=MY_ACCESS_TOKEN"

Facebook Graph Object Types Supported Out-of-the-Box

RestFB can map JSON to any class that has fields annotated with the @Facebook annotation, which is discussed in more detail in the JSON Mapping Rules section.

However, for your convenience, all basic Graph API Object types have their own Java implementation in the com.restfb.types package.

You may use these builtin types if you wish, or you can subclass them to add additional fields or functionality, or just write your own if you have special requirements. The builtins come with reflective implementations of toString(), hashCode(), and equals(Object o) to make your life simpler. If you subclass any of the builtins and add additional fields with accessors, those fields will be automatically taken into account by the above 3 methods.

The builtin types are as follows:

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(contains = Integer.class)
  List<Integer> numbers;
}

As of RestFB 1.4, Java to JSON mapping is supported by default via 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. When converting from Java to JSON, you never need to use the contains attribute of the @Facebook annotation - it's always ignored because enough information is available at runtime to perform the conversion correctly.

A note about Java to JSON mapping with the Graph API

This feature is only really useful for Old REST API users who connect to Facebook with LegacyFacebookClient. The Graph API currently does not require you to send any parameters as JSON, but this may change in the future.

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 FacebookClient methods throw the checked, 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.

String query = "SELECT name FROM user WHERE uid=220439 or uid=7901103";

try {
  List<User> users = facebookClient.executeQuery(query, User.class); 
} catch (FacebookJsonMappingException e) {
  // Looks like this API method didn't really return a list of users
} catch (FacebookNetworkException e) {
  // Looks like an error occurred at the network level
  System.out.println("API returned HTTP status code " + e.getHttpStatusCode());
} catch (FacebookGraphException e) {
  // Facebook API returned a specific error
  System.out.println("Call failed. API says: " + e.getErrorMessage());
} catch (FacebookResponseStatusException e) {
  // Old-style Facebook error response.
  // The Graph API only throws these when FQL calls fail.
  // You'll see this exception more if you use the Old REST API
  // via LegacyFacebookClient.
  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 FacebookClient, 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:

FacebookClient facebookClient = new DefaultFacebookClient(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 executeGet(String url) throws IOException {
      return new Response(HttpURLConnection.HTTP_OK,
        "{'id':'123456','name':'Test Person'}");
    }
  }, new DefaultJsonMapper());

// Make an API request using the mocked WebRequestor

User user = facebookClient.fetchObject("ignored", User.class); 

// Make sure we got what we were expecting

assert "123456".equals(user.getId());
assert "Test Person".equals(user.getName());

Links

Visit RestFB's home on Google Code.

Have a question not covered here? Ask in the RestFB Google Group.

Found a bug or have an enhancement request? Create a ticket in the issue tracker.

Download a read-only copy of the current source code using Subversion:

svn checkout http://restfb.googlecode.com/svn/trunk/ restfb

RestFB Users

If you're a happy RestFB user and would like to add a link to your site, please send me an email.

About

RestFB is written by Mark Allen and sponsored by Transmogrify, LLC.

Transmogrify is a Philadelphia-area software shop specializing in product design and Java, .NET, iPhone, and iPad software development. Our experience runs the gamut from embedded systems to Oracle DBA work to the latest in web and mobile technologies. Give us a call at (215) 436-XMOG or send an email to product@xmog.com if you need a hand with anything software-related. We can handle every part of the software lifecycle, from initial requirements and design to ongoing maintenance and support.

Facebook

Mark Allen likes

TransmogrifyTransmogrify
Create your Like Badge

Development of RestFB was aided by

Licensing

RestFB is open source software released under the terms of the MIT License:

Copyright (c) 2010 Mark Allen. 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.