Client and API isolation
At a first glance, such setup may seem more as a way to complicate things rather than help, but let me explain how it really help us.
For me, one of the biggest advantages of having more than one app are the independent deployments. We deploy some of our applications a lot (even several times a day), but we rarely touch the most important and stable parts of our infrastructure. Similarly, I can deploy a web client several times and if I break something, the API still works correctly, which is great because the API may be used by other clients as well.
But is it just about deployment? Of course not! It may seem that we need to have 2 apps running during development: the API and the web client itself. Most of the time this is not the case. As I mentioned above, we use CORS, which allow us to make requets to other domains. When I work on the client, I usually connect it to the production API. Have you ever needed to copy part of the production database, clean sensitive data and use it in development to catch any issues with the data you haven’t thought about? Not anymore! During development we can be connected to staging or production API or to the local API server.
Recently we wanted to show the changes in the client in some easy way, without the need to deploy to the staging server. The idea was simple:
- after finishing the build on travis, upload the assets to the S3 bucket using travis artifacts
- when a parameter is passed to the web app (like: `https://travis-ci.org?alt=my-feature-branch), start serving assets from S3 for a given branch, also set cookie to serve alternative assets on subsequent requests
defaultbranch name is passed as a param, revert to the regular assets, which are currently deployed
Now I can just push my feature branch, wait for a build on travis to finish and pass the url to someone to whom I would like to show the results. It’s so much better than just using staging! That way it behaves more naturally as it uses production data and I don’t even need to deploy anything.
You may be wondering if this is safe? In fact I’m constantly using the client in development to use the API, won’t I break anything? It is a valid question and in the case of some applications it could be risky. Imagine that you’re working on an admin application frontend. If you plug it to the production API you may accidentally fire a request, which will make a mess.
However, in our case it’s not a problem. The API does not let you do almost any destructive things and even if it did, in the worst case we would mess up our own accounts.
As I mentioned earlier, we use CORS, to make such isolation possible. CORS is fairly
simple to set up and use. When you fire up an ajax request to the different domain,
the browser should automatically try to use CORS. Before making the actualy request,
OPTIONS request should be sent in order to check if an endpoint accepts CORS.
In order to see how it works in action with Travis API, you may try to use such curl request:
curl --verbose --request OPTIONS \ --header "Accept: application/json; version=2" \ https://api.travis-ci.org/jobs
The response should look something like:
< HTTP/1.1 200 OK < Access-Control-Allow-Credentials: true < Access-Control-Allow-Headers: Content-Type, Authorization, Accept, If-None-Match, If-Modified-Since < Access-Control-Allow-Methods: HEAD, GET, POST, PATCH, PUT, DELETE < Access-Control-Allow-Origin: * < Access-Control-Expose-Headers: Content-Type, Cache-Control, Expires, Etag, Last-Modified < Content-Type: text/html;charset=utf-8 < Content-Length: 69 < Connection: keep-alive
It basically specifies how should browser behave when issuing a request to such endpoint, ie.:
- which HTTP methods can it use? (
- which headers can a browser sent with the actual request? (
- what places can a request come from? (
- can a browser send credentials with
For more info on CORS, you should check out the (CORS specification)[http://www.w3.org/TR/cors/].
Our API is written in Sinatra and CORS support is fairly easy to implement. Of course you may need a bit more code if you want to pass different options for different endpoints.
On the client side, we don’t have to do anything, the browser will just do the proper thing.
As you may noticed, there is one small disadvantage of using CORS. A browser
needs to fire an additional
OPTIONS request before making the actual request.
Handling such a request is really fast, because most of the time you don’t
need to do anything more than setting up a few headers, but you pay the cost
of doing the request anyway.
Depending on your situation it may or may not be a real problem. Thankfully, the web is moving forward and with growing popularity of SPDY and with the HTTP2.0 spec in the works, the number of requests should have smaller and smaller significance.
It’s also good to know that not all of the browser support CORS. If you want to use it, you may want to check if you can ignore the browsers which does not support it.
API and client isolation is awesome and you should try it!
I would also like to remind all of you reading this post, that I work on Travis full time thanks to Engine Yard, they’re sponsoring Travis CI and a lot of other OSS projects!