After reading the REST API Design Rulebook by Mark Masse, I'm all jazzed up about API design! This recent enthusiasm has afforded me the motivation to attack a number of new topics all at the same time. If I'm going to be experimenting with API design, why not try it on an Amazon EC2 (Elastic Compute Cloud) Micro instance? And if it's on EC2, why not try it using Node.js? And, if it's on a remote server, why not look into how Cross-Origin Resource Sharing (CORS) works with jQuery and remote REST APIs? This last question - cross-domian AJAX requests - is what I'd like to touch on in this post.
CORS (Cross-Origin Resource Sharing) is both simple and complex. It is quite simple in philosophy; but, the devil, as always, is in the details. I need to give a huge thank-you to Nicholas Zakas and Remy Sharp for clearing up the CORS workflow on their respective blogs.
CORS is AJAX. What makes CORS special is that the AJAX request is being posted to a domain different than that of the client. Historically, this type of request has been deemed a security threat and has been denied by the browser. With the prevalence of AJAX and the transformation of thick-client applications, however, modern browsers have been evolved to embrace the idea that critical information doesn't necessarily come from the host domain.
Now, modern browsers (Internet Explorer 8+, Firefox 3.5+, Safari 4+, and Chrome) can make AJAX requests to other domains so long as the target server allows it. This security handshake takes place in the form of HTTP headers. When the client (browser) makes cross-origin requests, it includes the HTTP header - Origin - which announces the requesting domain to the target server. If the server wants to allow the cross-origin request, it has to echo back the Origin in the HTTP response heder - Access-Control-Allow-Origin.
NOTE: The server can also echo back "*" as the Access-Control-Allow-Origin value if it wants to be more open-ended with its security policy.
Depending on the complexity of the cross-origin request, the client (browser) may make an initial request - known as a "preflight" request - to the server to gather authorization information. This preflight request can be cached by the client and is therefore not needed for subsequent CORS requests.
NOTE: Not every type of CORS request requires a preflight request; furthermore, some browsers do not support this concept.
Rather than diving deeper into the actual HTTP request headers (on which I am not an authority), I'd rather just get into some code that makes use of CORS (Cross-Origin Resource Sharing) AJAX requests.
Before we look at the Node.js server, let's take a look at the client-side jQuery code that performs the CORS requests. In the following code, I'm making both a PUT and a DELETE request to the Node.js server. Notice that I am chaining these two requests in serial; this allows the preflight request to be made on the first CORS request but not on the second:
demo.htm - Our jQuery-Powered CORS Demo
As you can see, there's nothing really special going on here. I'm using jQuery to execute AJAX requests in the same way that I always would. The only difference is that I'm posting the AJAX requests to a different domain (and port number).
When we run the above code, we get the following page output:
As you can see, the PUT request is preceded by an OPTIONS request. This OPTIONS request tells the client if a CORS request will be allowed; and, for those requests, which methods (GET, POST, PUT, etc.) can be executed. This OPTIONS request is cached by the client for a server-defined amount of time. This is why only one OPTIONS request needs to be performed despite that fact that two CORS requests (PUT and DELETE) and being run.
Now that we see how little the client-side code has to change - hardly at all - let's take a look at the Node.js server to see how the security handshake works. For this demo, the Node.js server understands two concepts: 1) OPTIONS requests and 2) everything else. If the request is not an OPTIONS request, the Node.js server simply echos back the request body that was posted.
server.js - Our CORS-Enabled Node.js Server
As you can see, the Node.js server either responds with a 204 No Content response for OPTIONS requests; or, it responds with a 200 OK response for everything else.
As far as I'm concerned, this is some pretty cool stuff! CORS (Cross-Origin Resource Sharing) AJAX requests may not be supported by every modern browser; but, if you're working on API design, like I am, client-side support is not necessarily a must-have - rather, it's a nice-to-have. And, with such little overhead, it's not too hard to implement.
Want to use code from this post? Check out the license.