Streamlining web app development with Stubby
If you’re a web developer, chances are you spend a lot of time hooking up applications to various JSON driven web services. Unless you have a test back end already in place, you need to be able to mock the responses your app expects.
There are a few ways of doing this: The simplest method is to point all calls you will make to
.json files stored in your project. When you’re ready to integrate with the back end, swap the references over to the real URLs.
In a perfect world, this would work fine. The problem arises when you need to quickly switch between local and deployed environments (which you inevitably will). Changing these manually in a big app would be long winded and awkward.
By defining references to both environments on every HTTP call, we’re able to refer to them based on an environmental variable —a global development boolean, for example. This mitigates the need for us to rewrite any code, and when deployed, the correct value will be used.
xhr.open('GET', isDevelopment ? 'json/response.json' : '/api/response');
This approach works well, but still it has some flaws. Firstly, it’s verbose. You need to write two references for each request, plus you’ll be deploying redundant code. It works, but it gets messy.
The complexity stems from having to perform logic every time we make a request. If we serve our test responses from the same relative paths as our deployed version, we circumvent the problem.
We can do this with a local web server, configured the same as our real one. The best way I’ve found is to use a tool called Stubby, which lets us define endpoints with YAML syntax. I’m using the node version, but there are .NET and Java equivalents.
It can be installed with NPM (with the global flag!):
$ npm install -g stubby
This isn’t anything groundbreaking, the same can be done manually with any server side framework. But this is fast, and really easy!
- request: method: GET url: ^/api/my/endpoint/$ response: headers: content-type: application/json file: sample-json.json status: 200 latency: 450
The syntax is self explanatory. We define the HTTP verb, url, headers, status code, and metadata. The data being returned can be a reference to a file or defined inline. By creating multiple
response properties, we can cycle through varying responses.
You can also simulate HTTP latency, which stops you forgetting that HTTP calls aren’t instantaneous (as they are when pointing to a file on disk). This feature makes your local set up behave much more like your live version. It’s also very useful for implementing loading spinners!
Stubby runs on its own Node server, so we need a way of marrying it to our main dev server. We can do this using a proxy.
I'm using Gulp to start up a server with Node and Express. Using the proxy-middleware package, a proxy can be set up with one line of code.
// requests to /api are proxied to localhost:8882 server.use('/api', proxy(url.arse('http://localhost:8882/api')));
Without doing this, the relative URLs would be pointing to the wrong server. Also, even if the correct server port was specified, we’d run into the cross origin restriction (presuming the
Access-Control-Allow-Origin: * header isn't present).
Live data locally
One of the benefits I've found most useful is the ability to test live data on my local machine. To do this we need to use a global variable with our apps URL. In the code below I've used a
var, however it's a great example of where
const could be used, were browser support more wide spread.
var ENV = 'http://application-url.co.uk/'; $http.get(ENV + 'api/resource', callback);
Now, all calls will be made to the absolute path. There's a little overhead doing this, but it allows us to really easily switch between and diagnose issues with different environments. Providing the
ENV variable is only ever an empty string when deployed, there's no problem.
If you do run into the cross origin restriction, you can run Chrome from the command line with the disable web security flag to bypass it.
$ open -a /Applications/Google\ Chrome.app --args --disable-web-security
With both servers up and running and the proxy in place, we've make the process of building and testing an app much simplier. With virtually no changes we can run our in three environments – locally with local data, locally with live data, or live with live data!