JimmyG | Blog | Book | Life | Projects | Contact

Understanding JSON-P

Posted: Thu 26th Nov 2009, 1:43pm
Tags: javascript

A lot of the time, very simple concepts can be obsucred by the simplifications frameworks make. In this article I want to explain how JSON-P works.

Usually when making an API call using JSON you can just use the browser's XHR object but the browser's security model (the same origin policy) means that you can only get JSON from the same orign that the page was served from. For mashups where you are pulling JSON from different origins this doesn't work.

Luckily there's a hack. The browser doesn't restrict where <script> tags pull their source from. This means you can get JSON data from a different server by adding a <script> tag to your page like this:

<script type="text/javascript" src="http//example.com/api/json"></script>

Traditional JSON APIs just return JSON data. Let's imagine a http//example.com/api/json returns JSON for a list of blog posts. This means that after the browser has loaded the script you will have the equivalent of this:

<script type="text/javascript">
{
    'posts': [
        'http://example.com/post/345',
        'http://example.com/post/2342',
        'http://example.com/post/5',
    ]
}
</script>

This is great, it works no matter where the JSON is served from, but wouldn't it be better if an event could be triggered when the data was loaded?

To do that, rather than returning the JSON data, the server could return the JavaScript source for a function call which passed the JSON data in as an argument so that the generated source looks like this:

<script type="text/javascript">
callback({
    'posts': [
        'http://example.com/post/345',
        'http://example.com/post/2342',
        'http://example.com/post/5',
    ]
})
</script>

Now when the code is loaded, the callback() function will be executed. At the moment there isn't a function called callback() so when using JSON-P you need to define one first. Here's an example:

<script type="text/javascript>

var callback = function(data){
    alert('There are '+data['posts'].length+' posts.')
}

</script>

Now, because a function called callback() exists, when the JSON-P is loaded and evaluated, the callback() function will be executed with the JSON data as its only argument.

You now have a way of executing a callback with data from any domain. That's JSON-P.

Custom Argument Improvement

Now you might have seen JSON-P implementations where you can specify the name of the callback as a query string part of the URL. If you are loading more than one data source as the same time this is very handy because you'll need to give your functions different names so that the correct callback is called and the server needs to know the name of the callback so it can generate JavaScript which executes the correct function. There are no hard and fast rules about how to pass the callback name to the server, but typically it is done as ?callback=callback-name.

Here's the same example but with a different callback name:

<script type="text/javascript>

var count_posts = function(data){
    alert('There are '+data['posts'].length+' posts.')
}

</script>
<script type="text/javascript" src="http//example.com/api/json?callback=count_posts"></script>

Notice that the JSON-P script tag is after the code that defines the callback. You wouldn't want the callback to be called before it has been defined.

This time the output from http//example.com/api/json?callback=count_posts might look like this with a different callback name:

count_posts({
    'posts': [
        'http://example.com/post/345',
        'http://example.com/post/2342',
        'http://example.com/post/5',
    ]
})

Most JavaScript frameworks support JSON-P. They do so by dynamically generating the <script> tag and the callback function based on the data you give them so that working with JSON-P feels very similar to working with normal AJAX, but allows you to get data from servers with different origins.