Cross domain API calls using Sinatra + pmxdr
You have an API server running and want to enable people to make cross domain XHR requests using javascript. How do you do that? There are bunch of methods that exist. I will present a technique to achieve this using HTML5's awesome postMessage API. I used pmxdr library, which is a pure javascript library that leverages HTML5's postMessage functionality without needing Flash or anything of that sort. Very neat!
Some setup details -
- My remote server is running on mylocal.name:9393 (mylocal.name -> localhosts in the /etc/hosts file). This server provides a POST operation on /api/token and returns a JSON object on a successful POST operation.
- My client is running on localhost:9494. It will make a cross domain request for the JSON token data from the remote server.
- Both my servers are powered by Sinatra.
Server settings
There are two things the server has to do before anything else.
- Have a valid route /pmxdr/api which loads the pmxdr-host.js file from the library. We need to add the approved domains to this file, but more on this later.
The view pmxdr.erb just loads the pmxdr-hosts.js file.get '/pmxdr/api' do p "got a get on pmxdr/api" erb :pmxdr end
- The server must allow cross domain through X-Frame-Options header. For Sinatra, which automatically addes X-Frame-Options header, you can configure it by
#x-frame-options allow sinatra configure do set :protection, :except => :frame_options end
These two things will get us started on the server side. The API token call is show here to give a context.
post '/api/token' do
p "post operation"
content_type :json
{ :token => '9238928fkjkjkdjfser'}.to_json
endIn the pmxdr-hosts.js (which is rendered by pmxdr view in this example), allow localhost with any port to access its resources.
var alwaysTrustedOrigins = [
/^[\w-]+:(?:\/\/)?(?:[\w\.-]+\.)?localhost\:\d+(?::\d+)?$/ // any origin on any protocol that has a domain that ends in localhost:anyport
];If you don't do this, the logs in the server will show a POST operation, but pmxdr host will not return the data and give a disallowed-origin error.
{pmxdr: true, error: "DISALLOWED_ORIGIN", id:"123", status:200, statusText:"OK"}Making API calls through Client
In this example, when you navigate to the client, an API call is made and the token is returned.
Load the pmxdr client code - <script src="pmxdr-client.js" type="text/javascript"></script>
The actual client side code is very simple.
var pmxdrInterface = new pmxdr("http://mylocal.name:9393/");
pmxdrInterface. function() {
this.request([{
method : "post",
uri : "/api/token",
data : "",
callback: handleResponse,
headers : {},
timeout : 30000 /* == 30 seconds*/
}]);
}
function handleResponse(response) {
//alert(response.data);
// do stuff with response
console.log(response);
var code;
codeContainer = document.createElement("pre");
if ( !response.error ) {
code += "\n\ndata:\n"+response.data;
} else code += (errorIsGood? "(PASS) " : "(FAIL) ")+"Error: "+response.error;
//codeContainer.appendChild(document.createTextNode(code));
results.appendChild(document.createTextNode(code));
//results.appendChild(document.createElement("hr"));
//
pmxdrInterface.unload();
}
pmxdrInterface.init();This is what happens in the above code -
- When pmxdr is initialized, a temporary iFrame is setup, that has a source of http://mylocal.name:9393/pmxdr/api. Remember - When we were setting up the server, we made sure that the API end point was "gettable" and it loaded the host library code.
- After the iFrame is intialized, it sends a GET request with a uri parameter is http://mylocal.name:9393/api/token and a randomly generated id.
- After this request, the pmxdr host checks if the requesting window (in this case localhost:9494) is allowed to view the data. Remember - the entry we added to the hosts.js file.
- A temporary iFrame, which is setup gets deleted after a successful response or the timeout that is specified in the above code.
The great thing is that you can send any kind of custom headers that your API call should respond to (for authentication or anything like that).
You can download the entire code from my git repo https://github.com/saranyan/pmxdr-sinatra



