The PhantomJS WebServer module lets you create self contained web applications that are easy to deploy to heroku using the PhantomJS build pack.
I’ll be talking about this at Oxford geek nights on the 7th of August - come along if you’re in the area.
(tl;dr - deployed example here & more involved app here)
Let’s start with a base PhantomJS script - this loads the Oxfordshire lanyrd page and outputs the names of any upcoming events:
This script can be run with phantomjs example.js
and it will print the names of all upcoming events in the terminal - something like this:
Upcoming Events in Oxfordshire: * Oxford Geek Night 32 * WitneyMeets * XML Summer School 2013 * Sterling Geo Intergraph ERDAS UK UGM * All Your Base Conference 2013 * jQuery UK 2014 * World Humanist Congress 2014
…super cool. Have a look at the quick start guide on the PhantomJS wiki to find out how this works and what other things are possible.
Using the webserver module
To expose this script with the webserver module, you have to add a few things:
This can be run in the same way as the previous script - phantomjs example.js
- then when you visit localhost:8080, you should see the list of events in your browser.
With phantomjs, you’re not limited to sending plain text back to the client - you can render images of the webpage and send that back (either by reading the file back with the File System Module, or using base 64 to send back an embeddable data-uri).
Deploying
There is a PhantomJS Buildpack for heroku which makes deploying lovely.
To get your app ready for deployment you have to do a few things:
Set the port based on environment variable PORT
Add a file named Procfile
containing the command to spin it up:
web: phantomjs example.js
Commit your files to git then create a heroku app with the build pack
heroku create --stack cedar --buildpack http://github.com/stomita/heroku-buildpack-phantomjs.gitCreating quiet-lowlands-5118... done, stack is cedar BUILDPACK_URL=http://github.com/stomita/heroku-buildpack-phantomjs.git http://quiet-lowlands-5118.herokuapp.com/ | [email protected]:quiet-lowlands-5118.git Git remote heroku added
Push your code up to heroku
git push heroku masterCounting objects: 10, done. Delta compression using up to 4 threads. Compressing objects: 100% (7/7), done. Writing objects: 100% (10/10), 1.34 KiB, done. Total 10 (delta 2), reused 0 (delta 0) -----> Fetching custom git buildpack... done -----> PhantomJS app detected -----> Fetching PhantomJS 1.9.0 binaries at http://stomita-buildpack-phantomjs.s3.amazonaws.com/buildpack-phantomjs-1.9.0.tar.gz -----> Extracting PhantomJS 1.9.0 binaries to /tmp/build_2idj4c8tadrpx/vendor/phantomjs -----> Discovering process types Procfile declares types -> web Default types for PhantomJS -> console -----> Compiled slug size: 15.5MB -----> Launching... done, v5 http://quiet-lowlands-5118.herokuapp.com deployed to Heroku To [email protected]:quiet-lowlands-5118.git * [new branch] master -> master
The example app should now be available on the reported url (in this case: http://quiet-lowlands-5118.herokuapp.com).
View Deployed Example Site GitHub Source
A more involved example
I’ve put together a more complex version of this style of app - it allows you to specify any webpage, renders a screenshot and returns some information about the page (a list of links).
It also serves a static page with a form to submit the requests to the app. It’s deployed to phantomjs-webserver-example.herokuapp.com and the source code is on github.
I’ve tried to make it an easy project to modify for your own use - so fork away and have a hack!
A few issues / gotchas
- Mongoose (the embedded server) doesn’t parse the POST parameters with the default jQuery
contentType
header ‘application/x-www-form-urlencoded; charset=UTF-8’; I had to drop the charset and it seemed to work okay. - Firefox has trouble parsing large data-uri strings in json objects, so I’ve split the image and json on separate lines and decode them when the request comes back (unfortunately Firefox fails to add the xhr header that fixes the mongoose error)
- GET parameters aren’t parsed. I’d much sooner use a GET request for this app, as there’s not any state change and it would allow the responses to be cached. In wtcss I fudged this parsing.
- Sometimes the page render returns a blank image, especially when on heroku and under stress. This is a known issue - a work around is to wrap the .render in a setTimeout.