Home & blog  /  2012  /  Mar  /  view post  /

JSON-P: what it is, what it's not and how it works

posted: 23 Mar '12 23:24

I thought I'd take time out from discussing the brave new world of ECMA5 (to be continued) and do a post on JSON-P, since it's occured to me lately that it's often misunderstood by intermediate developers.

This post is aimed at developers who use JSON-P but have been been too sure about what it does under the bonnet. We'll pin down what it is, isn't, how it works, and some common misconceptions.

JSON-P in two sentences

JSON-P is means of loading data from a remote server, provided the server in question is expecting the request. It is not AJAX, and does not necessarily involve JSON.

How JSON-P works

JSON-P gets around the fact that JavaScript's Same Origin Policy prevents cross-domain AJAX by exploiting the fact that the src attribute of the script tag is not subject to this limitation, and can load in content (i.e. JavaScript) from remote servers.

Therefore, the steps of a JSON-P request are as follows:

  • a new script tag is DOM-scripted into the page (or an old one is re-used)
  • the request URL is applied to the script tag's src attribute
  • the requested server loads our request and outputs a response
  • the response is evaluated as JS, since we requested via a script tag

1var req = 'http://www.someserver.com/web_service';

2var script = document.createElement('script');

3script.src = req;

4document.head.appendChild(script);

If the server's response is...

alert('hello from someserver.com!');

...the alert will fire in our page once the request completes. Likewise, if the response is...

var something = 'hello from someserver.com!';

...a global variable, something, will be set in our page.

The key to understanding JSON-P is to remember that the server's response ultimately ends being evaluated as JavaScript, since it is called by a script tag.

These are simple examples. Usually, of course, you're going to want the server to give you some data.

Accessing the server response: assignment and callback

Normally we want our JSON-P request to result in one of two things happening:

  • the data is passed to a globally-accessible callback function that we've prepared
  • the data is assigned to a global variable

It must do one or the other, otherwise the response will not be accessible to our page's JavaScript. So if the server simply returned:

{name: 'Fido', type: 'Labrador', age: 3}

...that data would be unreachable - even though it's valid JavaScript. This is not a trait of JSON-P but of JavaScript itself, but it is a common stumbling block for developers new to JSON-P. Think about it; if a linked script in your page contains the above, without assigning it to a variable or passing it to a function, it is inaccessible.

Assignment

We saw variable assignment in the example further up. This is useful when, say, loading jQuery from CDN; we don't need the request to call a callback - we simply want jQuery to load. In other words, our JSON-P response should simply assign the variable jQuery (the jQuery namespace on which the library's API lives).

Callback

More common is for the response to call a callback function that we have prepared. For this, the server will need to know the name of the callback function; large, public JSON-P web services allows you to specify this in your request structure.

For example, the following is a request to Twitter's JSON-P web service to retrieve five Tweets that mention Paddington Bear:

'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=my_callback_func'

If you run that in your browser, you'll see the web service outputs JS that passes the returned data, as JSON, to the callback I requested, my_callback_func().

Imagining the Twitter web service in simple terms, if it was built in PHP, it would look something like this: (I include this only for context; if you're a front-end-only developer, don't worry too much about this).

1//get JSON-encoded Tweets

2$tweetsJSON = get_tweets_json();

3    

4//output

5if (isset($_GET['callback'])) echo $_GET['callback']."(";

6echo $tweetsJSON;

7if (isset($_GET['callback'])) echo ");";

The web service fetches and JSON-encodes the tweets, then builds an output string consisting of a call to our callback function, passing it the JSON as its only argument, i.e.

my_callback_func({ /* Twitter JSON here */ });

If we hadn't specified a callback, only the JSON would be output - useless for JSON-P requests but usable by server-side cross-domain requests (not relevant to this article).

As I touched on above, it is not always certain that you'll need to tell the web service the name of your callback function. If you control the web service, you may choose to hard-code the name of the callback on the server, so our PHP would look like:

1$data = get_some_data(); //get data and format as JSON

2echo "jsonp_callback(".$data.");";

There, the server assumes jsonp_callback - so your callback will have to be called that. This is a less common scenario, but illustrates the point that, whilst the ability to stipulate the name of your callback to the web service is a convenience, it is not a fundamental component of the JSON-P concept.

Callbacks with jQuery

We've established that our callback needs to be globally accessible. Given that, you might wonder how this, a typical JSON-P request with jQuery, works:

1$.ajax({

2     url: 'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=?',

3     dataType: 'jsonp',

4     success: function(json) { console.log(json); }

5});

There, our callback is an anonymous function, clearly not globally accessible. What we don't see, though, is that jQuery redefines it globally, assigning it a randomly generated function name (to minimise name clashes with other global entities).

This is why we stipulated the name of our callback function as ? in the request URL rather than choosing it ourselves (though we could have done). This permits jQuery to handle this whole issue; sure enough, Opera's Dragonfly tools shows the actual request URL that was sent, and the global function that was created:

Misconceptions

Now we've taken a tour of what JSON-P is and how it works, some of the common misconceptions about should now seem obvious when you read them.

Misconception 1: JSON-P is a form of AJAX request

[UPDATE: granted, this depends on your definition of AJAX. I would hold that it's not a form of AJAX, but see the comments below for a discussion on this...]

This misconception is understandable for two reasons. Firstly, JSON-P involves the silent loading of data, just like AJAX. Secondly, jQuery implements JSON-P through its AJAX module, something that is understandable (i.e. to have a single section of the API concerned with data retrieval/submission) but can and does lead to this misconception.

As we've seen, though, JSON-P works by exploiting the src attribute of script tags, whereas AJAX requests are done over XMLHTTPRequest.

Misconception 2: JSON-P always involves JSON

The original proposal for JSON centred on the idea that servers would, on receipt of a JSON-P request, go off to the database (where applicable), get some rows of data, and serialise and output that data as JSON.

This is often the case - but it's not to say it has to be. The server response could equally be a string or a number, say.

Misconception 3: JSON-P requests must involve a callback

Again, most JSON-P web services do involve callbacks, but it does not have to be the case. As we saw early on, there's no need for callbacks when, say, loading jQuery over JSON-P - we simply want jQuery's jQuery global variable to be set.

Misconception 4: callbacks must be global functions

It's true that the callback function (or assigned variable) must be globally accessible, but this does not necessarily mean global in sense of being in the outermost scope.

1<script>

2function func() { } //global function

3var namespace = {};

4namespace.func = function() {}; //globally accessible function

5</script>

If you're using a namespace pattern like above, i.e. where your entire code exists in a single namespace to avoid global pollution, there's no reason your callback function can't be a method of that namespace. So our Twitter request from above might become:

'http://search.twitter.com/search.json?q=Paddington%20Bear&rrp=5&callback=namespace.func2'

Likewise if the web service sets a variable rather than calling a callback. You could have the server do this:

1$data = get_some_data(); //get data and format as JSON

2echo "namespace.jsonp_data = ".$data.";";

So there you have it

So there you have it. If JSON-P was hazy for you before this, I hope I've helped clear up the issue and shown you how it works under the scenes. It's certainly one of the lesser understood elements of every-day web development, but it pays to understand precisely what's going on.

For more on JSON-P, be sure to check out Kyle Simpson's JSON-P.org.

post new comment

Comments (10)

Samson, at 25/03/'12 21:13, said:
Thanks for this very enlightening piece. What threw me at first was the name json-p, the fact that it involves cross domain requests and its very commonly used with the jQuery AJAX helper method.

Your article clears up the ambiguity. As a matter of fact I have used json-p to return an XML file (had to wrap it in a callback of course), then used a XML parser to convert it back to XML, and viola I had an XML object to traverse. 

Question for you: 
Does the json-p mechanism have a way of detecting cases where you don't get a response from a server? Or would you use jQuery's $.ajax (which has the error property) if you need to act on a failed request?
Prinzhorn, at 26/03/'12 16:54, said:
AJAX doesn't need to involve XML,
JSON-P doesn't need to involve JSON,
JavaScript has nothing to do with Java.

Oh boy, the world is a fucked up place :-D
Kyle Simpson, at 26/03/'12 17:00, said:
Thanks for the article. I appreciate you bringing to light some of these discussion points.

However, I somewhat disagree with how you about how *you* define JSON-P. I run http://json-p.org as an advocacy attempt at defining a better (and safer) JSON-P officially, so I have some strong opinions on the topic.

Firstly, I take issue with the notion that JSON-P does not necessarily include JSON data. Yes it does! That's why it's called JSON-P. Ask Bob Ippolito, the one who first formally proposed JSON-P in 2005, and I'm sure you'll hear that it is intended to be "JSON with padding".

You see, all native data types (including strings and numbers) *are* valid JSON. Check the official definition at  and you'll see that JSON doesn't have to be wrapped in {} or [] to be JSON. It's JavaScript-compatible syntax for native data types... THAT is JSON. JavaScript data is JSON. And therefore, that is the data that is transferred via JSON-P. To suggest JSON-P doesn't have JSON at its heart is to miss the point completely.

Secondly, I disagree philosophically with the stance that JSON-P is not Ajax because it's not using the XHR interface. JSON-P is a form of requesting data (just as Ajax is), and it returns the same types of data that Ajax could (except for binary data).

JSON-P is a form of Ajax because Ajax is no longer a term that means simply using the XHR object to get XML (as it meant at the beginning). Ajax is now a general term for asynchronously fetching data from a server via any of several different techniques, JSON-P being just one of them. It's needlessly confusing to draw a distinction which says JSON-P is not Ajax. It is!

I think you'd be more helpful and instructive if you simply focused on distinguishing the different transport methods for Ajax, where XHR and JSON-P are just two of many. They have different implications, for sure, but philosophically, they're all just ways of getting data from a remote source asynchronously, and they should be considered that way.

------

What's more interesting about JSON-P, which you do briefly point out, is that it is, at the moment, indistinguishable from real JavaScript. This is quite unfortunate because it makes it so insecure. That's why my efforts to define a stricter/safer subset of JS for JSON-P are, I think, important.

I would like to see more focus on what we need to do to "fix" JSON-P to make it a more useful and trustable technique.
Kyle Simpson, at 26/03/'12 17:02, said:
(ps. your blog improperly formatted my links in my previous comment. can you correct it? hopefully you can also get that bug fixed in your blog software)
Mitya, at 26/03/'12 17:28, said:
Hi Kyle - thanks a lot for your feedback. I'm pleased to have someone so concerned with the workings and development of JSON-P commenting on this article, even if I can't agree with you entirely.

You are correct that JSON is not only about object literals but a popular definition of JSON is precisely that. Rightly or wrongly, therefore, there is a point to be made here - that JSON-P is not only about JSON in its popular definition.

After all, as I said at the start of the article, this post was pitched at developers who have used JSON-P but perhaps aren't savvy with its inner workings. In other words, intermediate developers. Had it been aimed at higher level developers, your point would have been well suited to the article.

As for JSON-P being a form of AJAX, I think this is a subjective point. I have some sympathy for this viewpoint; it serves the same purpose, and is a silent load. But its differences are also marked: the fact it requires remote server cognisance and complicity, the fact it cannot be done synchronously, the fact it requires a DOM.

I deliberately didn't take the article in the direction of security as I feel that is a whole separate point that would have detracted from the point of my article - to clear up how JSON-P works. But you are quite right; a more secure version of JSON-P is a truly exciting prospect.

Thanks again
p.s. thanks for the heads up re: the links thing; now fixed (it's my own system. What was I thinking?)
Kyle Simpson, at 26/03/'12 19:32, said:
Mitya-
I appreciate your response.

"JSON-P is not only about JSON in its popular definition"

I honestly think the point you were making is valid, just mis-labeled. The point is that any kind of JS data is technically JSON, so I would have just said that JSON-P allows JSON data transfer, including any kind of valid JS data (even those data which don't "look" like traditional JSON objects).

The real confusion is not that people should know that JSON-P can include "non-JSON" data (there's no such thing), but that JSON itself includes data that may not "look" like what people are used to calling JSON data. When you make that clarification (a useful concept even to mid-level devs), then it's clear why it's called JSON-P.

"the fact it requires remote server cognisance (sp)"

In practice, XHR for Ajax is most often used for getting JSON data, which means there's a JSON API endpoint on the server. In other words, most Ajax also has server cognizance.

Sure, there are some uses of Ajax which are just for getting file contents (HTML, CSS, etc), but there are also corner-cases of JSON-P where what's being fetched is more about the code than the data, and where in that usage, the server has little cognizance that a JSON-P request was made of it.

Bottom line, I think *this* distinction you're making is not that useful in practice, and could end up confusing readers.

"cannot be done synchronously"

Actually, it technically can be done synchronously. a script-tag in the markup of the page can be a by-definition valid JSON-P type of request (I've done this many times). Moreover, you can make synchronous requests for scripts using document.write() during the loading of a page (even if it's not in the markup). You can also do synchronous requesting of scripts (which include JSON-P like data) inside of Web Workers using importScript.

Point being, the sync/async thing isn't terribly accurate or useful in practice either.

"it requires a DOM"

in most cases, sure, but not with importScript in web workers, as just mentioned.

of your points, though, using the DOM for the request as opposed to an XHR object in memory is not a terrible point to make. it's useful to know that difference. i just don't think that's enough to say "JSON-P is therefore not Ajax". I think it clearly is Ajax, just a different flavor with some different ingredients.

"direction of security"

I understand why you left out security, and it was probably the right choice for this audience. I was just pointing out that I think the biggest problem with JSON-P is in fact that, so I expected an article all about JSON-P to at least mention it (or perhaps point at my site for more info!). :)

Like I said before, I think it's a good and useful article, and I don't think I strongly disagree with your stance, but I just wanted to document for posterity (here in the comments) some points of further clarification that I think help readers get a more full perspective.

Thanks again for the article! :)
Tien Do, at 27/03/'12 04:28, said:
Thanks, it's the best post about JSON-P I've ever read.
Mitya, at 27/03/'12 11:37, said:
Thanks, all.

 - I appreciate the discussion. This is clearly an area you are passionate about and I have sympathy with some of your points.

I think the question of whether JSON-P may be considered AJAX is definitely not a clear-cut one. Clearly you think it is, while I believe there are implicit and explicit differences. It's an interesting point either way.

 - with JSON-P you can't easily detect failure (another reason I would separate it from AJAX) because if the remote server doesn't respond, or you got the URL wrong, the request is lost to the ether. With AJAX, the callback mechanism is handled on the part of the requester, i.e. your code, so there is a promise you can depend on. With JSON-P, you are dependent on the server - there is no promise.

The usual, inelegant workaround is to use a timeout or interval to wait a period of time and then assume failure. But this of course doesn't confirm what happened (404? Server's DB down? etc). The advent of window.performance, though, would at least give you means to retrieve any error code that fired (404 etc), but that's obviously not cross-browser.
Sam Luden, at 6/04/'12 22:12, said:
AJAX is not an ideal or a concept, it is a specific term referring to the technique of loading data outside the context of a full page request by the user agent, using javascript. Your argument here is silly, like AJAX is some kind of hippy state of mind that defies labels or a culture movement. It isn't.

Loading data with javascript? You're doing AJAX, congrats. JSONP = AJAX. XHR request = AJAX. Etc.
Mitya, at 7/04/'12 12:38, said:
@Sam - thanks for your feedback. Leaving aside the usual sarcasm and unnecessariy sense of hostility common of many developers (at least when behind a keyboard), I would contend that my stance is not 'silly'.

The bottom line is I feel there are sufficient and notable differences between AJAX as in XHR, and JSON-P, to warrant their being defined separately and, in the least, for this to be a debatable (rather than black and white) point.

After all, AJAX is not a definitive beast; asynchronous JavaScript and XML, it stands for. Done any JSON-P lately loading XML? No. Done any SYNchronous requests lately? Yes, of course. Disagree as you will, and you are entitled to, but the point, as we are demonstrating, is worthy of debate.
post new comment