How to implement an awesome JavaScript JSONP API in 4 easy steps

Recently I was given a project at work which had to get some data from a server on a different domain through JavaScript. We all know that this is not possible because of the same origin policy, however JSONP is here to help us hack around the problem. JSONP stands for JSON with padding and it’s incredibly simple, yet powerful way of requesting data from a server on a different domain.

Let’s suppose you want to build a public API that returns how awesome is something. 

You want to provide something like:

how_awesome.is('@filipminev'); // Returns: Uber rad!

I’ll show you how to do it in 4 easy steps.
Let’s get down to bee’s knees.

1. JS API wrapper
I assume you will most probably want to build an API which has one or more public functions (API methods) and several private (internal) ones. I’ve chosen to present the Module Pattern, because it provides a clean way of having private variables and functions. You can read more about it here.


(function (window, document, undefined) {
    // We pass the window, document and undefined as parameters
    // so that the references to those global variables
    // inside the closure do not have to perform a scope look-up.
    // (tiny performance gain)

var how_awesome = (function (window, document, undefined) { var api = {}, _callID = 1; // Private variable
// Public API function api.is = function(thing) { // // Code goes here... // };
// Private function function _parseJSON(jsonStr) { // // Code ... // }
// Global function window.awesomeCallback = function(jsonStr) { // // Callback code // };
// Returning the api variable // exposes the 'is' function as public, // everything else stays private return api;
})(window, document);
// We make 'how_awesome' a global variable window.how_awesome = how_awesome; })(window, document);

2. JSONP request
We create a JSONP request via a script element injection - create a new script tag with src attribute = the destination of the data we have to fetch.


// Public API function
api.is = function(thing) {
    var src = '';

// Remote domain from where we need to request data src += 'http://how.awesome.is/?';
// We want to know how awesome is the thing src += 'thing=' + encodeURIComponent(thing);
// Callback and callID are explained later src += '&callback=awesomeCallback'; src += '&callID=' + _callID;
var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.async = true; script.id = 'awesome_' + _callID; script.type = 'text/javascript'; script.src = src;
// Appending the script to the head makes the request! head.appendChild(script); _callID++; };

3. Create JSONP response
Let’s assume our JSONP request ended up on this really simple PHP script, which we will call awesomeness.php.


<?php
    $howAwesome = array(
        'Stinks like unwashed socks',
        'Meh',
        'Not to shabby',
        'Auwesome',
        'Uber rad!' 
    );

if (isset($_GET['thing']) && isset($_GET['callback']) && isset($_GET['callID'])) {
$callbackData = array(); $callbackData['callID'] = $_GET['callID']; $callbackData['thing'] = urlencode($_GET['thing']);
$asciiSum = 0; $thing = $_GET['thing'];
for ($i = 0, $len = strlen($thing); $i < $len; $i++) { $asciiSum += ord(substr($thing, $i, 1)); } $callbackData['awesomeness'] = $howAwesome[$asciiSum % 5];
// That's how we create the JSONP response!! echo $_GET['callback'] . "('" . json_encode($callbackData) . "')"; return; }

As you can see we create the JSONP response just by wrapping the json encoded data by the callback specified in the JSONP request. In that way we return JavaScript code which is then interpreted by the browser. It will try to find in our case a global function named awesomeCallback.

4. Handle JSONP response
As mentioned above, now the browser will call the global function awesomeCallback with the JSON string as a parameter. So let’s define the function, I’ll put it in our API wrapper closure since it uses the private function _parseJSON.


window.awesomeCallback = function(jsonStr) {
     // Parses JSON string to JSON object
    // See function definition below
    var json = _parseJSON(jsonStr);

// API call clean-up // Removes the injected script from the DOM, // that's why we needed a callID parameter // that we pass in the JSON request var elem = document.getElementById('awesome_' + json.callID); elem.parentNode.removeChild(elem);
// Print the result! var thing = decodeURIComponent((json.thing+'').replace(/\+/g, '%20')); console.log(thing + ' is ' + json.awesomeness); };

Since I decided to make this example JSON API framework agnostic, we need a function that parses the returned JSON string into a JSON object. That’s the definition of the private function _parseJSON:


function _parseJSON(jsonStr) {
    if (typeof jsonStr !== "string" || !jsonStr) {
        return null;
    }

// Has JSON.parse built in? if (window.JSON && window.JSON.parse) { return window.JSON.parse(jsonStr); }
// Logic borrowed from https://github.com/douglascrockford/JSON-js/blob/master/json2.js if (/^[\],:{}\s]*$/ .test(jsonStr.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { return (new Function( 'return ' + jsonStr))(); } throw new SyntaxError('JSON.parse'); }

Voilà! Our awesome JSONP API is complete! You can check out the complete source on GitHub.

Another very intriguing topic, which I would like to discuss with you in the comments below, is: How do we secure a JavaScript JSONP API like that one? Any suggestions?