Getting around CORS with Node.js

I have recently wrote a JavaScript web application that depended on AJAX and a Java driven back end. The back end was a standard  RESTful Web service running an a Glassfish server. Before I started working on the application a designer was hired to make the HTML/CSS layout that was desired. As I got further into the development process and the moving parts started to change the CSS also needed to change. This proposed a problem for us mainly because all of the data ( and the html elements needed to display it) that were needed to populate each page had to be requested from the Web service. Typically what I would do is log in to the web application running on the test server fired up firebug and use the HTML/CSS edit feature to get the CSS to where I needed it to be. Alternatively, I would save the website and open the HTML and CSS pages locally and edit from there. Any CSS changes would then get committed to the repo. However, this wasn’t a good enough way for the designer. The designer wanted to put the repo on their localhost and simply run the application on their local server. This of course caused a CORS error because an XHR request could not be made from localhost to a remote server.

To solve this problem the designer could have got a local copy of the back end working on his localhost as well. However that would involve mysql, eclipse and glassfish to be configured correctly. Not really ideal. So I went out in search for another solution. After talking to some people I decided to use node.js. Havent yet worked with node I wasn’t quite sure of what I was going to do. Some people said i needed a Proxy some said i needed Middleware but at the end of it all I just needed a simple server and a client.

The Structure

The idea is for a server to listen to the requests coming in from your localhost on a particular port. This means that the URL of the XHR requests has to be changed to localhost:portnumber. Once the request is captured, a dummy client will make the same exact request but instead of the client being your localhost, it will be a node client whose domain is the same as the domain of the back end. Create a client using the domain of the back end. I never really found any documentation of what the createClient function accepted so let me show you what I used:

var aclient = http.createClient(80, 'backendurl.com');

The next step is to create a server that listens on a particular port. This is the same port i mentioned above. Ensure that this port is not being used by another application running on your computer otherwise you will get a weird port not available exception. Now, in the function that you pass in when you create the server you need to capture the request, get the needed data and make a similar request with a new url. You also need to figure out what the request method is. This is important because sometimes there will be an OPTIONS method sent which is basically a way of the browser testing if CORS is allowed.

if (req.method === 'OPTIONS') {
	// add needed headers
	var headers = {};
	headers["Access-Control-Allow-Origin"] = "*";
	headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
	headers["Access-Control-Allow-Credentials"] = true;
	headers["Access-Control-Max-Age"] = '86400'; // 24 hours
	headers["Access-Control-Allow-Headers"] = "X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept";
	// respond to the request
	res.writeHead(200, headers);
	res.end();
} else if (req.method === 'GET') { // no data is coming
	// use the client you created to make a request, this request will basically
	// need all of the information captured in this GET request  coming from your localhost:portnumber
	var clientrequest = aclient.request(req.method, '/api' + req.url, {
		'host': 'backendurl.com',
		'authorization': req.headers['authorization'],
		'content-type': 'application/json',
		'connection': 'keep-alive',
	});
	clientrequest.end();
	var msg = "", clietheaders;
	// get the response from the back-end
	clientrequest.on('response', function (clientresponse) {
		clientheaders = clientresponse.headers;
			clientresponse.on('data', function (chunk) {
			msg += chunk;
		});
	});
	setTimeout(function () {
		// send the data you just received from the back end back to you
		// client application on localhost
		res.writeHead(200, clientheaders);
		res.write(msg);
		res.end();
	}, 500); // wait a bit just in case we don't have all of the chunks of data
}
This is a simple implementation and it works great. It might not be the perfect solution but it gets the job done. Feel free to contact me if you need to implement this type of solution. Also if you need to go ahead and join the node.js irc channel: #node.js on the irc.freenode.net server. The people there are really helpful and forgiving.
Advertisements