Pylons and Facebook Application Layout
Sunday, May 30th, 2010While I spent quite a bit of time deciphering the Graph API documentation and the OAuth guides that Facebook puts forth and submitted three documentation fixes for examples that call non-existent parameters and consequently don’t work, I came to the realization that my original layout really only works if you use a single Pylons instance per Facebook application. Since we’re focused on Don’t Repeat Yourself (DRY) Principles, some thought needs to go into things.
First, our platform needs to be designed. For this set of projects we’re going to use Nginx with uwsgi. Since we’re serving static content, we’re going to set up our directories on Nginx to allow that content to be served outside our Pylons virtual environment. Tony Landis was one of the first to provide an implementation guide for uwsgi with Nginx for Pylons which provided some of the information needed to get things working.
Our theoretical layout looks like the following:
/webroot |--- /static |--- /fb /virtualenv |--- /fbappone |--- /fbapptwo
Later we’ll add a CDN that does origin pulls from /webroot/static. This application would have worked wonderfully with Varnish and ESI if the ESI could be compressed, but, setting up Nginx -> Varnish -> Nginx -> uwsgi seemed somewhat inefficient just to add compression. The Facebook application we’ve developed is an IFrame canvas which took roughly fifteen hours to debug after the original concept was decided. The majority of that time was spent dealing with the IFrame canvas issues. FBML was much easier to get working properly.
What we end up with is a url structure like:
http://basedomain.com/ /static/ (xd_receiver.html, jquery support modules, CSS files) /fb/ (Generic facebook files, support, tos, help) /(fbapp)/application_one/ /(fbapp)/application_two/
As a result of this structure, we don’t need to manipulate config/routing.py as the default’s set by Pylons map things the way we want. In the /static/ directory, we can put our CSS, js and static media files. Remember to minify the CSS and js files and combine them if possible.
Our nginx config looks like:
server { listen 1.2.3.4:80; server_name xxxxxx.com; access_log /var/log/nginx/xxxxxx.com-access.log; location ~* (css|js|png|jpe?g|gif|ico|swf|flv)$ { expires max; } gzip on; gzip_min_length 500; gzip_types text/plain application/xml text/html text/javascript; gzip_disable "MSIE [1-6]\."; location ^~ /static/ { alias /var/www/xxxxxx.com/static/; } location ^~ /fb/ { alias /var/www/xxxxxx.com/fb/; } location / { uwsgi_pass unix:/tmp/uwsgi.sock; include uwsgi_params; } }
We could modify the nginx config to pull / from the static page, but, we’re actually capturing that with a root controller that knows what applications reside below it as a directory of sorts.
We used Debian which doesn’t support uwsgi yet. A brief set of instructions follows which should work on any Debian based distribution as well:
apt-get install libxml2-dev dpkg-dev debhelper cd /usr/src apt-get source nginx wget http://projects.unbit.it/downloads/uwsgi-0.9.4.4.tar.gz tar xzf uwsgi-0.9.4.4.tar.gz cd nginx vi debian/rules add: --add-module=/usr/src/uwsgi-0.9.4.4/nginx/ \ dpkg-buildpackage dpkg -i ../nginx_0.7.65-5_i386.deb mkdir /usr/local/nginx/ cp /usr/src/uwsgi-0.9.4.4/nginx/uwsgi_params /etc/nginx
/etc/nginx/uwsgi_params, add:
uwsgi_param SCRIPT_NAME /;
Note: I had problems with 0.9.5.1 and paster enabled wsgi applications which caused issues with Pylons.
Our uwsgi command line for development:
/usr/src/uwsgi-0.9.4.4/uwsgi -s /tmp/uwsgi.sock -C -iH /var/www/facebook/ --paste config:/var/www/facebook/fpapp/development.ini
One of the things that made Facebook integration difficult was somewhat incomplete documentation or even incorrect documentation on Facebook’s site. While the Graph API is new, it is quite a bit more powerful. While they do have official support, I think I’ll use velruse for OAuth integration next time and use the Python-SDK for the Graph API integration. See my previous post on using Pylons for a Facebook Application for a little more detailed information on how to get the application working.