5 Facebook Application Gotchas
Everyone and their uncle is writing Facebook applications for the new Facebook Platform. I, too, have my own offering, written by myself and the other OpenHive guys: Bookshelf. Even though the platform was released almost a month ago there are still plenty of tricks, gotchas, and other undocumented oddities that deserve to be brought to light.
Gotchas, tips, and tricks
-
The Timeout
For those who know what I'm talking about already the answer is 12 seconds. Everyone else read on.
Facebook canvas pages (URLs of the form http://apps.facebook.com/yourapp/foo) work on a proxy model. In the application configuration you specify a callback URL so that when someone visits http://apps.facebook.com/yourapp/foo Facebook in turn requests http://mydomain.com/myapp/foo. Facebook fetches the FBML from your callback URL and renders it on the canvas page.
If your callback takes too long to respond Facebook spits out this ugly message:
There are still a few kinks Facebook and the makers of <application name> are trying to iron out. We appreciate your patience as we try to fix these issues. Your problem has been logged - if it persists, please come back in a few days. Thanks!
Ignore the fact that this error message is awful (try back in a few days?!), for now. I did some testing (i.e., a PHP file and a call to sleep) and found that the timeout is set to around 12 seconds. Although it should never, ever take this long to render any webpage, if you're doing a lot of processing you might run afoul of this limit, so watch out.
-
The Load
Because canvas pages work on a proxy model your servers will have to handle the load Facebook throws at it. For some apps, like iLike, this means growing from zero to three million users in a week. If you plan on creating a popular app then you'll need to plan and benchmark for high concurrency situations.
To start you should make sure your database is well optimized. Read my article on MySQL optimization tips for some ideas of what that means — most of the tips are database neutral.
Second you should use a tool like ab with the concurrency set high and try to maximize your requests served per second. In short, if you're going to be hosting a popular Facebook application be prepared to deal with Facebook-magnitude loads.
-
The Session
Before you can talk with Facebook you must initialize a session using the Facebook class provided by the Facebook API library. You cannot tell if the session is valid by whether the session_key field in your object is null — sometimes it looks completely valid but has actually expires. The REST client will throw an exception if you try to do anything with an invalid session, so it's something to avoid.
You can get your session data you can call auth_getSession(). It returns an array that contains the timeout so you can check directly if the session has expired. If the timeout is set to 0 then the session lasts forever. You can also use try/catch to make sure your sessions are valid:
$fbuid = $facebook->get_loggedin_user(); if ($fbuid) { try { if ($facebook->api_client->users_isAppAdded()) { // The user has added our app } else { // The user has not added our app } } catch (Exception $ex) { //this will clear cookies for your app and redirect them to a login prompt $facebook->set_user(null, null); $facebook->redirect($_SERVER['SCRIPT_URI']); exit; } } else { // The user has never used our app }
The above will guarantee that you always have a valid session. (Thanks to Aditya for information about session expiration.)
-
The JS
The Facebook Platform supports three means of dynamic, client-side content: iframes, flash, and javascript wrappers. By using iframes you are essentially given free reign to do what you will. Flixster uses Javascript in an iframe to create its UI elements, for example.
Flash is flash and can be embedded using the fb:swf FBML tag. The Javascript wrappers, however, are where the gotchas pop up. Facebook supports three pieces of Javascript functionality: showing a DOM element, hiding a DOM element, and replacing the contents of a DOM element with HTML returned from a remote URL.
You can show, hide, or toggle an element with id foo by giving an element clicktoshow, clicktohide, or clicktotoggle attributes with the value foo, respectively.
To swap out the content of an element with remote content use clickrewriteurl and clickrewriteform. The first parameter contains the URL and the second parameter is the id for a form element containing parameters to pass to the URL. You can combine clicktoshow, clicktohide, and clicktotoggle in a single element but cannot combine these with clickrewriteurl.
To get around this you can mark it up as follows:
This is useful to, for example, show a progress indicator or "Saving..." text as you process something asynchronously. Make sure to test this in all major browsers since I've seen this fail in IE under circumstances.
-
Using Lighttpd
lighttpd is an increasingly popular webserver. It is much lighter than Apache at the expense of Apache's modularity and extensibility. A common scenario would be to use it for serving static content.
However, many people are using it in place of Apache as a full, dedicated webserver. The problem arises when you try to submit large amounts of data via POST to a Facebook canvas page. If the data is large enough Facebook will send your app an Expect: 100-continue header, which lighttpd doesn't understand. This results in lighttpd throwing an HTTP 417 error (pretty obscure, eh?), which Facebook spits right back in the users face.
To get around this you need to either use something besides lighttpd which does support the 100-continue header (e.g., Apache) or submit the data directly to your server and then redirect to the Facebook after the data is processed.
The Facebook Platform is still young and changes weekly. Keeping abreast of the changes can be daunting, so let me know if this helped at all.