Skip to content

Instantly share code, notes, and snippets.

@tchak
Last active September 16, 2015 09:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tchak/3d28e8af06d7101127d1 to your computer and use it in GitHub Desktop.
Save tchak/3d28e8af06d7101127d1 to your computer and use it in GitHub Desktop.
Ember Error Handling RFC

Ember data will define several type of errors :

DS.AdapterError < Ember.Error
DS.InvalidError < DS.AdapterError
DS.TimeoutError < DS.AdapterError
DS.AbortError < DS.AdapterError

An DS.AdapterError has a errors property that contains errors payload as defined in json-api

Adapter have to decide on the type of error to throw.

In case of error we going to expose throwed error on error attribute of the record.

record.get('isError') // true
record.get('error') // DS.AdapterError

record.get('error.errors') // [{title: 'Server Error', status: 500}]

The store on commit rejection checks if an error is a DS.InvalidError (or a subclass) and in this case will extract error messages by looking a source.pointer attribute of each error in the json payload.

Adapter will implement a hook buildError that will take a json-api errors document and decide on the type of error to throw. Main distinction is DS.InvalidError for “user recoverable” errors with messages and other type of “system” errors. Default implementation is to check for 422. But different apis can have different strategies to distinguish the type of error.

We need to make clear the distinction between inValid state and isError state. The first is for “user recoverable” type of errors. The second is for “system is broken” type of errors. Maybe we need a better name then isInvalid? Current name makes reference to validation errors and can be confusing. We feel that validations are a different problem and are a controller/component concern.

The mapping of attributes to error messages is exposed as DS.Model#errors on the record.

record.get('errors').get('firstName') // ['title should not be empty']
record.get('error') // DS.InvalidError

record.get('error.errors') // [{title: "Invalid Attribute Error", details: 'First name should not be empty', status: '421', source: {pointer: 'data/attributes/first_name'}}]

On the adapter level we are going to use json-api error format to communicate with the store. Main goal is to stop leaking jqXHR objects to adapter hooks. It will make it easier to move to fetch when the time comes.

RESTAdapter implements a new hook :

/**
  @method handleResponse
  @param  {String} status
  @param  {Object} headers
  @param  {Object} payload
  @return {Object | DS.AdapterError} json response payload or an error
*/
handleResponse: function(status, headers, payload) {}

Default implementation will use two boolean hooks isSuccess and isInvalid. A response wich is not one of them or that thrown an exception is concidered an error and will put the record in isError state.

/**
  @method handleResponse
  @param  {String} status
  @param  {Object} headers
  @param  {Object} payload
  @return {Boolean}
*/
isSuccess: function(status, headers, payload) {}
/**
  @method handleResponse
  @param  {String} status
  @param  {Object} headers
  @param  {Object} payload
  @return {Boolean}
*/
isInvalid: function(status, headers, payload) {}
/**
  @method ajaxSuccess
  @deprecated
  @param  {jqXHR} jqXHR
  @param  {Object} payload
  @return {Object} json response payload
*/
ajaxSuccess: function(jqXHR, payload) {}
/**
  @method ajaxError
  @deprecated
  @param  {jqXHR} jqXHR
  @param  {String} responseText
  @param  {String | Error} errorThrown
  @return { jqXHR }
*/
ajaxError: function(jqXHR, responseText, errorThrown) {}
@tomdale
Copy link

tomdale commented Jun 5, 2015

An DS.AdapterError has a data property that contains errors payload as defined in json-api

data seems like a pretty non-specific property name to me. I'm generally in favor of keeping terminology in line with JSON API, but in this instance it seems like we could use a more specific term.

@tomdale
Copy link

tomdale commented Jun 5, 2015

In general, I wonder if there is some normalization we should be asking adapter authors to do for error information. Every RFC or discussion I've seen around errors has had a primary part of the API be exposing what the server returned into app space. I'm not opposed to offering the ability to thread data from server into the app, but we generally try to avoid that as the happy path.

Perhaps it is outside the scope of this proposal, but I would suggest we consider exposing common details about an error (status code? description?) in a reliable way, so that users' Ember Data is more portable even across differing adapters.

@tomdale
Copy link

tomdale commented Jun 5, 2015

The second is for “system is broken” type of errors. Maybe we need a better name then isInvalid? Current name makes reference to validation errors and can be confusing. We feel that validations are a different problem and are a controller/component concern.

I agree here. Not sure of the exact nomenclature, but this was the point I was trying to make in the call earlier. Invalid errors are a specific instance of "retryable" errors. One distinction is that some retryable errors can be fixed via time (server overloaded, please try again in 30 seconds) and some are only retryable after user intervention (your password was too short, please make it longer).

@tomdale
Copy link

tomdale commented Jun 5, 2015

On the adapter level we are going to use json-api error format to communicate with the store. Main goal is to stop leaking jqXHR objects to adapter hooks. It will make it easier to move to fetch when the time comes.

😍 😍 😍

@tomdale
Copy link

tomdale commented Jun 5, 2015

RESTAdapter will have two hooks

Not sure I understand the exact semantics of ajaxSuccess and ajaxError, can you expand on them a little bit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment