JSONP explained

I had heard about JSONP several times before, but as I didn't need to understand it yet, I had just ignored any of the usual reference materials about it... until today. I was trying to access some feeds via ajax and noticed that even if they "loaded", with readyState being 4 and all that, the response body was empty. I first wondered if it was because I was executing my script locally, and maybe the feeds host wouldn't like to see clients using localhost servers, who knows?

I was wrong, it was just the usual cross domain problem. There are three solutions:

  1. use an internal proxy to fetch data and send it to your javascript code. For example: proxy.php will fetch all the external feeds on behalf of your code.js, which will call proxy.php for all its requests. This, apart from adding extra load in your server, defeats the whole purpose of doing client side scripting for retrieving the external feeds data.
  2. use a flash proxy and communicate with it from your javascript code. Not something I want to do, since I didn't want to use flash for this.
  3. use JSONP

And what is JSONP? In essence, dynamically creating a new SCRIPT element in your document. Its URL will be the URL from the feed service; and when it finishes loading, it will execute. But here's the trick: the URL has a special parameter, usually named callback. This parameter is used in the response from the server, in such a way that the response is actually a function call whose parameter is the JSON data you expect.

Confusing, huh? OK, it's easier with an example. Let's say you want to grab the latest pictures from the tortoises pool in flickr. It has an RSS feed: http://api.flickr.com/services/feeds/groups_pool.gne?id=78811852@N00&lang=en-us&format=rss_200

Notice the format=rss_200 parameter?

If you change it to format=json you get something like this:


jsonFlickrFeed({
        "title": "Tortoises, turtles and terrapins - zoo, wild or pets Pool",
        "link": "http://www.flickr.com/groups/tortoise/pool/",
        "description": "<a href=\"http://flickr.com/groups/78811852@N00/\"><img src=\"http://bighugelabs.com/flickr/profilewidget/group/random/000000/ffffff/78811852@N00.jpg\" alt=\"Tortoise. Get yours at bighugelabs.com/flickr\" title=\"Tortoise. Get yours at bighugelabs.com/flickr\" /><\/a> A group for, you guessed it, Tortoises, turtles and terrapins, they can be wild, zoo or pets. There is a group for pets only - <a href=\"http://www.flickr.com/groups/pet_tortoise/\">&quot;PET TORTIOSE&quot;<\/a> Comment code below, Seen in <a href=\"http://www.flickr.com/groups/tortoise/\">&quot;Tortoises, turtles and terrapins - zoo, wild or pets&quot;<\/a>",
        "modified": "2009-07-28T20:37:25Z",
        "generator": "http://www.flickr.com/",
        "items": [
       {
            "title": "Schildkröten_2009",
            "link": "http://www.flickr.com/photos/testudograeca/3762164698/in/pool-78811852@N00",
            "media": {"m":"http://farm3.static.flickr.com/2573/3762164698_6f27527c81_m.jpg"},
            "date_taken": "2009-07-26T12:54:13-08:00",
            "description": "<p><a href=\"http://www.flickr.com/people/testudograeca/\">TestudoGraeca<\/a> has added a photo to the pool:<\/p> <p><a href=\"http://www.flickr.com/photos/testudograeca/3762164698/\" title=\"Schildkröten_2009\"><img src=\"http://farm3.static.flickr.com/2573/3762164698_6f27527c81_m.jpg\" width=\"240\" height=\"166\" alt=\"Schildkröten_2009\" /><\/a><\/p> <p><a href=\"http://www.testudograeca.de\" rel=\"nofollow\">www.testudograeca.de<\/a><\/p>",
            "published": "2009-07-27T13:57:24Z",
            "author": "nobody@flickr.com (TestudoGraeca)",
            "author_id": "27736205@N04",
            "tags": "shop online info terrarium heizung schildkröte warstein schildkröten gehege türkische ibera photovoltaik testudograeca landschildkröte frühbeet freigehege quarantäne maurische gartenbeleuchtung winterstarre auswinterung dirkvöllmeke gartengehege schildkrötenunterschlupf pflanzsteine frühbeetheizung danielastockhausen wwwschildkröteonlinede"
       },
...
});

We aren't finished yet. Did you notice the jsonFlickrFeed( piece at the beginning? That is the function call I wrote about earlier. If we included this as an script in our page, for example like this:


var url = 'http://api.flickr.com/services/feeds/groups_pool.gne?id=78811852@N00&lang=en-us&format=json';
var script = document.createElement("script");
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
document.body.appendChild(script);

when the script was loaded, it would execute and call the jsonFlickrFeed function. But the call would fail, because there isn't any function with that name.

This is why the callback parameter is used in the URL. Let's say you have a function like this:


function jsonphandler(data)
{
    alert("hey! jsonphandler was called ");
    // do something with data
}

If we append jsoncallback=jsonphandler to the previous pool URL, the response will begin with jsonphandler( instead of jsonFlickrFeed(, and our own function (i.e., jsonphandler) will be called when the script is loaded.

To sum things up: JSONP is all about loading a new SCRIPT element, which calls a function that you have specified beforehand. It still needs a cooperating server in order to work, but since SCRIPT tags aren't subject to the same cross domain restrictions as XMLHttpRequest calls (AJAX for buzz words lovers), this is the only solution you have if you want to pull data from a different domain without using a proxy.

A word of caution, though: unlike simple RSS retrievals, this actually allows the remote server to inject any javascript code they wish in your page, because you're liberally loading whatever they return to you. A malicious server could return other javascript code instead of the simple function call you expect. Therefore, you should use this only with servers you can really trust.