Rendering errors with JSON and Rails

I’ve seen bad practices for handling errors with JSON and Rails:

  • Not using proper HTTP statuses;
  • Not using the error callback; and
  • Making errors message building harder than necessary.

Let’s look at some examples and how to fix them.

Not using proper HTTP statuses and the error callback

Callers of your API should know whether their request worked by looking at the response’s status code, not by looking at the response’s body. When you don’t use status codes, every response looks like a successful response:

  • Server’s controller:

      def create
        @model = Model.new(params[:model])
        if @model.save
          render :json => @model.to_json
        else
          render :json => { :errors => @model.errors.full_messages }
        end
      end
  • Client’s JavaScript:

      $.ajax(
        // ...
          dataType: "json"
        , success: function(data){
          if (data.errors) {
            // do something with errors
          } else {
            // do something with successful data
          }
        }
      )

Instead, tell your API’s caller that their request failed by setting a status code for the error. If the error is the client’s fault, use a status code in the 400-499 range; if the error is the server’s fault, use a status code in the 500-599 range. Here’s a list of status codes, with information on when to use each.

  • Server’s controller:

      def create
        @model = Model.create(params[:model])
        if @model.save
          render :json => @model
        else
          render :json => { :errors => @model.errors.full_messages }, :status => 422
        end
      end
  • Client’s JavaScript:

      $.ajax({
        // ...
          dataType: "json"
        , success: function(data){
            // do something with successful data
        }
        , error: function(xhr){
            var errors = $.parseJSON(xhr.responseText).errors
            // do something with errors
        }
      })

By responding with the 422 status code, your client knows that the response failed. jQuery calls the error callback for you, removing unnecessary logic from your code.

Making error message building harder than it needs to be

I watched a “Backbone with Rails” screencast where the author had his server respond with @model.errors. Then he began to work on his client and he spent 30 minutes working on transforming the @model.errors data into the data that @model.errors.full_messages would have given him, free.

  • render :json => { :errors => @model.errors }

    Responds with a object where the keys are your model’s attributes with the associated errors:

      { name: ["is blank"], age: ["is not greater than 0"]}
  • render :json => { :errors => @model.errors.full_messages }

    Responds with an array of error messages, which you can render straight into a list in the DOM.

     ["Name is blank", "Age is not greater than 0"]