Thursday, November 21, 2013

Supporting JSON and HTML in RAILS

I really enjoy the modern development platforms. We are in the middle of a launching a new mobile application on the iPhone and supported by a backend web server written in Rails. The web server supports users connecting through a browser (.html) and from our phone application (.json). The question then becomes: how do we support both connection APIs with as little effort as possible?

For the HTML browsing, Rails strongly encourages an MVC architecture; the V (View) is critical here. The Model and Controller are internal components, controlled by our company and written by our development staff. The View is Public, accessed by our customers through a (public) standard protocol. Interestingly, despite the fact that our iPhone client is controlled and written by development within our company, it feels and acts more like something accessed by our customers through a (private) standard protocol. It is in this sense, that the JSON api used by our client should be treated as a View level component and handled by that part of the server architecture.

Once we think of JSON as a yet another View, the challenge then becomes how to build Views that support JSON. Enter RABL.

Nathan Esquenazi introduced RABL (Ruby API Builder Language) in his excellent blog post, If you’re using to_json, you’re doing it wrong. RABL provides a Ruby DSL for describing JSON and XML outputs in View files. He makes a great argument that supporting anything other than the simplest of APIs causes knotty code deep down in the Model, exactly where you don't want to support these types of changes (and breaking MVC principles). Here's another blog post in which he describes Building a Platform API on Rails.

Meanwhile, Ryan Bates created three excellent Rails Casts on JSON API creation.
  1. Cast #320 shows how to generate JSON using JBuilder.
  2. Cast #322 shows how to generate JSON using RABL.
  3. Cast #350 shows how to generate multiple JSON (API) versions using RABL.
At first blush, this last piece (API versioning) may seem strange to include as it (versioning) appears orthogonal to API building and generation. I'm quite comfortably stating unequivocally that the moment you publish an API, you will have to create a new version for it in the future. We might as well use a methodology that allows us to modify the API (through versioning) down the road.

With versioning, the question then becomes how the client best tells the server which version it wants/uses and vice-versa (in which version the server responded). To me, the best method (and the one allowing continued commitment to RESTful APIs) uses the Accept: header token to specify the version. Contrast this with placing the version in the URI (.../v1/... vs. .../v2/...). Peter Williams makes an excellent case for a header token in his blog post, VERSIONING REST WEB SERVICES. He uses XML instead of JSON. You can read his entire thoughts on RESTful versioning in his series of blog posts.

One other suggestion to be considered, Daniel Morrison suggests using a subdomain to divide machine requests from human requests (api.domain.com vs. www.domain.com). He shows how to do that at the end of his blog post, Building Awesome Rails APIs: Part 1. Search for "API Subdomain". Note, there is no Part 2. I like this, although I'm not convinced it's necessary and would like some more experience building and using APIs before trying it. If you want to test subdomains on your local machine, try using lve.me or lvho.st.

Finally, this piece represents a compilation of research on dual HMTL/JSON generation and in the context of API (JSON generation) versioning. Having not yet built a full scale system requiring this, I'm loathe to make too many recommendations. Stay tuned for future blog posts confirming which of these techniques works best.

That said, at this point I like the use of the Accept: header token to determine the content type of the View's response. I don't think I like using a subdomain, adding /api/ the URI path or appending .json to the request. For versioning, I favor mixing the version number into the header token over insertion in the URI path. (Thanks to Stephen Bennet for saving me from adding version to subdomains - you're right).

Finally, I plan to write a follow up post, shortly, covering some thoughts about JSON and data model "swizzling" as a security issue.

1 comment:

  1. In order to reduce attacks on Web Sessions, the Application Interface's Session Token should not be structurally similar to the Web Session. That is, it should not be possible to steal an HTML Cookie and transform it into an Application Session Token. If possible, one could engage in CSRF attacks via an Application interface using only the transformed Cookie, presuming Authenticity Tokens have been turned off to improve speed (eliminate "unnecessary" form request before form submission).

    ReplyDelete