Friday, June 20, 2014

How Backbone plays nicely with Rails 4 (by skipping the authenticity token and by formatting params properly)

Alright, the blog post title is a little misleading because Backbone doesn't really do either of what I mentioned. Backbone does NOT skip the authenticity token nor does it format params properly for Rails. But it's what I google'd when me and my co-worker were looking for the answers. So I titled it this way to help some peeps.

Alright, so again, credit does not go fully to me on this - my co-worker and I had to figure this out.

How does Backbone 'Skip the authenticity token'

Alright, so I was just starting to learn backbone with a very simple setup: Rails Server with a Backbone app. I was following the tutorial on backbonetutorials.com. So I got to the part where the code calls a 'save' on the model:

user.save(userDetails)

and it all worked - much to my surprise. I thought for sure there was an issue in that there's no way Backbone knew about Rails' authenticity_token. Sure enough, when I went to the Rails log, there was no authenticity token being passed in the params (as there is when you submit a rails built form). Furthermore, looking at the request in Chrome's network tab - nope, no authenticity token in the request parameters.

Yet - somehow, the request was succeeding? Did Rails somehow know this is a Backbone request and ignore the authenticity token?

Turns out - this has nothing to do with Backbone - and everything to do with jquery-rails (the version of JQuery that ships as a gem as part of Rails standard releases these days).

So upon further inspection of the request in Chrome's network tab - indeed there was no authenticity token in the request parameters - BUT there was a X-CSRF-Token in the request headers!!!

Ahhhh - so Rails must check both places (request params AND/OR request headers) for a authenticity token.

The question is - who is appending this token to the request headers?

Turns out, it's JQuery-Rails. JQuery-Rails appends this header to all AJAX requests where the CSRF meta tags are present.

https://groups.google.com/forum/#!topic/rubyonrails-core/eyTb_WZXLcs

Very cool stuff. So Backbone doesn't skip the authenticity token - it just makes an AJAX request, which JQuery-Rails is smart enough to append a authenticity token to the header of that AJAX request.

How does Backbone format its params properly

As long as I've been using Rails, I've always understood the POST CREATE request to accept its parameters in a specific way. If you had a user that had a name and age, you would need to pass the parameters like this:

{ user : {name : "Casey", age : 31 } }

In other words, you would need to nest the values within a object that's named after the resource. However, looking at Backbone, backbone has its parameters like this:

{ name : "Casey", age : 31 }

Notice that it's not nested inside "user". What's really weird, is if I look at the Rails log for the request of a Backbone.model.save, here's what comes in as the parameters:

{ name : "Casey", age : 31, user : { name : "Casey", age : 31 } }

It turns out that Rails (in its infinite magic) uses a module called ParamsWrapper that I can only theorize analyzes what comes in, and if it's only one-level deep, massages the params so that there is an extra param that's formatted properly for Rails (two-levels deep). This way, you have access to the params the way you passed them in:

params[:name]
params[:age]

and you have access to them the way Rails needs it:

params[:user][:name]
params[:user][:age]

Pretty crazy stuff.

Hope this helps some peeps.