Handling Rails HEAD 204 on update

This article was provided by kaisersly.

The problem

Rails default response to an update is:

head :no_content

which is an empty response with a status code of 204 (NO CONTENT).

Even if you write:

def update
	if @resource.update(params)
		render @resource, status: :ok
	end
end

you'll still get an empty response (HEAD) with a 204 status code.

When you do a DS.find('resource', id), js-data will send a PUT request. It will then receive a HEAD 204 response and will try to inject the payload in the cache. So it will perform something like:

DS.inject('resource', JSON.parse('')) // the '' comes from the empty payload in the HEAD response

and will throw an error (you can't inject an empty object).

The Rails solution

This stackoverflow question provides the Rails way to change the response.

The js-data solution

Step 1 : disable the injection

You can disable the injection when you update a resource.

Resource.update(1234, { foo: 'bar' }, { cacheResponse: false }).then(...);

You can disable it app wide:

var store = new JSData.DS({
  beforeUpdate: function (Resource, data, cb) {
    resource.cacheResponse = false;
    cb(null, data);
  }
});

or resource wide:

var Resource = DS.defineResource({
  name: 'resource',
  beforeUpdate: function (Resource, data, cb) {
    resource.cacheResponse = false;
    cb(null, data);
  };
});

If you use Angular (app wide) :

angular.module('myApp', ['js-data'])
  .config(function (DSProvider) {
    DSProvider.defaults.beforeUpdate = function (resource, data, cb) {
      resource.cacheResponse = false;
      cb(null, data);
    };
});

Step 2 : inject the resource manually

You just disabled the injection. If you stop here, this will happens:

var myResource = DS.get('resource', 1);
// { name: 'my resource', id: 1 }
myResource.name = 'a new name'
myResource.DSSave();

myResource = DS.get('resource', 1);
// still { name: 'my resource', id: 1 } because the updated resource wasn't injected

You have to inject the resource when the update is successful :

var myResource = DS.get('resource', 1);
// { name: 'my resource', id: 1 }
myResource.name = 'a new name'
myResource.DSSave().then(function (data) { // data, the payload of the response, is empty
  DS.inject('resource', myResource);
});

myResource = DS.get('resource', 1);
// { name: 'a new name', id: 1 }