How to handle failure to save on server?

Wondering what is the best way to handle a failure to save on the server-side? For example, suppose my Ember app calls record.save() and the server is unable to actually do the save, possibly for legit reasons (for example, the record was modified by someone else, or some sort of complex validation wasn’t met).

If my server returns a 4xx, this seems wrong, REST-wise, but is also problematic because the client-side copy of the model is now out of date.

If my server returns a 2xx, how is the client to know that the save failed? If I re-GET, the server returns a 304, leaving the client out of sync again.

When I’ve designed REST APIs before, I’ve typically included an envelope that would allow encapsulating this data, e.g.

{ 
 success: true,
 record: { the record as normal }
}
// vs.
{
  success: false,
  errorMessage: { "email address is already taken" }
}

This format doesn’t seem to play well with Ember Data, so I’m wondering if there’s a more canonical way that Ember Data expects this to work?

Take a look at DS.ActiveModelAdapter.ajaxError. The expectation is that your response will have a status of 422 Unprocessable Entity and the payload will be JSON with a top-level object of “errors”, such as:

{
    "errors" : {
        "emailAddress" : ["email address is already taken"]
    }
}

If you’re not using the ActiveModelAdapter, you can customize your own adapter by doing something like this:

App.ApplicationAdapter = DS.RESTAdapter.extend({
    ajaxError: function(jqXHR) {
        // copy the code from DS.ActiveModelAdapter.ajaxError
    }
});

The result of DS.ActiveModelAdapter.ajaxError ultimately sets the errors to your model so you can access them via model.errors and sends your model into an invalid state; however, from model.errors you can show which fields are invalid and force the user to fix them before being resubmitted. Attempts to resend the form before fixing the errored fields will throw an error because you cannot save a model in an invalid state.

HTH, Beez