Stefan Baumgartner

Web ops, performance and front-end

Using grunt-connect-proxy

20 September 2013 by @ddprrt | Posted in: Javascript, Tools, Workflows, grunt

Update 2014/01/13: The interface of grunt-contrib-connect has slightly changed. Please check my article on how to add middleware. The proxy middleware is still the same, tough.

With any application that communicates with some sort of backend interface exchanging JSON data, you're often in the need to use proxies in your own server configuration to overcome CORS restrictions (either that, or use jsonp, which always seems like a workaround to me). Previously --- in the times we worked with our local Apache servers -- it was always a bit tedious and also caused a lot of time running into the project setup for every team member. Now with our yeoman-grunt-bower setup, it mostly takes no longer than a minute. Cause guess what, there's a already Grunt-task for that.

The problem

Why do we need proxies? Well, I assume your app uses JSON for data transfer, so by calling an URL like http://someserver.com/API/Login you get all the data you need for your JavaScript application. And in most cases your application is located on the same server, which makes those calls really easy. However, if your are developing locally on your machine and try to call this server, you'll soon see that you won't come very far. Due to CORS restrictions you are not allowed to retrieve data from a different domain than yours.

Proxies help with that. They stand in for the server you want to reach, get the required data, while letting your browser think that you're all in the right domain. So instead by calling http://someserver.com/API you can call http://localhost/API.

Creating a proxy with Grunt

Task-runner Grunt has quickly become my most favorite tool when it comes to front-end development. It builds up on Node.js, which allows me to easily create new tasks, plus there are already a lot of great tasks out there which help me with my everyday development. Also for proxies, like the one created by Drewzboto.

Recently we switched our scaffolding process from a GitHub clone clusterf**k to a more streamlined process with Bower and Yeoman, and Yeoman heavily uses Grunt. So this short tutorial will show you how to add a proxy to your already existing Yeoman-like Gruntfile (with connect and livereload already set in place.

Installing

Coulnd't be easier. Just type

npm install --save-dev grunt-connect-proxy

With the parameter --save-dev the module gets added to your package.json, the one file you have to make available in your code repository for your co-developers.

Setting up a proxy

Search your connect task in your Gruntfile and add this little snippet:

connect: {
    ...
    server: {
        proxies: [
            {
                context: '/',
                host: 'someserver.com',
                changeOrigin: true
            }
        ]
    }
}

This snippet tells us, that any call which can't be answered by your machine will be forwared to someserver.com. There are a lot more options, you might also need https or port parameters for instance. Check out the GitHub repo for more information on that.

Add this part to the top of your Gruntfile.js, right before module.exports.

var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;

and call the proxySnippet middleware in your livereload configuration:

connect: {
    ...
    livereload: {
        options: {
            middleware: function (connect) {
                return [
                    lrSnippet,
                    mountFolder(connect, '.tmp'),
                    mountFolder(connect, yeomanConfig.app),
                    proxySnippet
                ];
            }
        }
    }
}

Now you're all set up. The last thing you've to do is to call configureProxies before using connect in your task, and everything's ready to go:

grunt.task.run([
    'clean:server',
    'concurrent:server',
    'configureProxies',
    'connect:livereload',
    'open',
    'watch'
]);

It's as easy as that. We added this to our main Gruntfile we use for scaffolding our projects, so everytime we need a proxy, it's just peace of cake instead of endless subdomain and subserver configuration in the old httpd-vhosts.conf file.

Directing to more than one server

This was actually a part which required a lot more thinking and understanding of proxies to get this done right. We had the -- at least for us -- rather specific use case to retrieve our data from more than one server at the same time. Mainly because there was one server handling the content, and one server which took care of all the user data. As you can see, the proxies option in the default task is actually an array, which means we can easily define more than one proxy configuration.

connect: {
    ...
    server: {
        proxies: [
            {
                context: '/user',
                host: 'userserver.com',
                changeOrigin: true
            },
            {
                context: '/',
                host: 'someserver.com',
                changeOrigin: true
            }
        ]
    }
}

Creating the new context user which should allow me to use http://localhost:9000/user to get to http://userserver.com. I put that one before the other on purpose, cause at the moment this will actually do nothing at all.

Reason is that the proxy can't decide why to redirect all the user calls to the other service. We need to define a redirection for the original proxy to make this happen:

connect: {
    ...
    server: {
        proxies: [
            {
                context: '/user',
                host: 'userserver.com',
                changeOrigin: true
            },
            {
                context: '/',
                host: 'someserver.com',
                changeOrigin: true,
                rewrite: {
                   '/user' : '/user'
                }
            }
        ]
    }
}

This rewrite rule tells the proxy that ever call which starts with user gets redirected to the user context we defined earlier. It won't work the other way round, since the context has to be defined beforehand, but that shouldn't matter actually. Feel free to include as many proxies and rewrites. Using this structure everything should work fine.

Bottom line

I've to admit that I've grown on Grunt. Not only that everything just seams so easy to create, it's also one definition that works on every workstation in our department. No need for petty set up phases, just everything is on the go and ready if another team member joins a project. Proxies have been the crux for each project, often leading us to switch to JSONP files just for convenience, and because the Backend guys could actually implement that much, much quicker. No need for that anymore.

Update

In the meantime Grunt's interfaces have changed and Yeoman's generators use the new grunt-contrib-connect package instead of the deprecated grunt-connect one that has been used before. This package includes livereload as a default, though it's not that easy anymore to add middleware than it was before. I addressed this topic in my recent article on The magic behind grunt-contrib-connect. Check it out and add your proxySnippet where it belongs!

Me again. The Gulp, Yeoman, Bower book is pretty sweet. Just saying.

Comments? Shoot me a tweet!