opbyiw
Last Updated: February 25, 2016
·
3.999K
· gonchs
4e0c9d494694758d998655327dc16657

Display ActiveRecord errors as i18n keys in Rails JSON API

Let's say you are creating an API for you Rails app and you want it to return json errors in a similar way as Github. By that I don't mean to mimic the exact same format of Github API but rather just a part of it - returning translatable string keys for certain type of errors instead of translated error messages.

Something like this:

{
  "errors": {
    "user.email": [
      "already_exists"
    ]
  }
}

You can define your own error types, but in my case, those similar to Github's were sufficient.

missing_field - This means a required field on a resource has not been set.

invalid - This means the formatting of a field is invalid. The documentation for that resource should be able to give you more specific information.

already_exists - This means another resource has the same value as this field. This can happen in resources that must have some unique key (such as Label names).

You can't just translate those errors in localization files for your language because those can be used for rendering error messages in erb templates. And you also probably don't want to render juts plain "invalid" in your forms.

I've found many unnecessarily complex ways to solve this and decided to do it in a simpler way. You can essentially do this by setting a locale to "json" (or any other name) for all json requests and then "translating" individual error codes. You can do this by creating a responder:

class ApiResponder < ActionController::Responder

  def json_resource_errors
    I18n.locale = "json"
    resource.valid? # this refreshes locale in which error messages are translated
    { errors: resource.errors }
  end
end

...,your "json" localization file:

json:
  errors:
    format: '%{attribute} %{message}'
    messages:
      blank: missing_field
      empty: missing_field
      equal_to: invalid
      taken: already_exists

...and then replacing default Rails responder in your API ApplicationController (assuming you have application controller separated just for API).

class ApplicationController < ::ApplicationController
  self.responder = ApiResponder
end
Say Thanks
Respond

1 Response
Add your response

14874
C46d9e1cbb008ff2d200ec584e673c56

Nice technique! I couldn't get it work with the ApiResponder class. Instead, I set the locale in a before_action in my API controller.

over 1 year ago ·