I'm writing a pretty standard CRUD RESTful API in Rails 4. I'm coming up short on error handling though.
Imagine I have the following model:
class Book < ActiveRecord::Base
validates :title, presence: true
end
If I try to create a book object without a title I'll get the following error:
{
"title": [
"can't be blank"
]
}
ActiveRecord validations are designed to be used with Forms. Ideally I'd like to match up each human readable validation error with a constant that can be used by an API consumer. So something like:
{
"title": [
"can't be blank"
],
"error_code": "TITLE_ERROR"
}
This can be both used to display user facing errors ("title can't be blank") and can be used within other code (if response.error_code === TITLE_ERROR
...). Is there any tooling for this in Rails?
EDIT: Here's a very similar question from Rails 2 days.
On error_codes.yml define your standard API errors, including status_code
, title
, details
and an internal code
you can then use to provide further info about the error on your API documentation.
Here's a basic example:
api:
invalid_resource:
code: '1'
status: '400'
title: 'Bad Request'
not_found:
code: '2'
status: '404'
title: 'Not Found'
details: 'Resource not found.'
On config/initializers/api_errors.rb load that YAML file into a constant.
API_ERRORS = YAML.load_file(Rails.root.join('doc','error-codes.yml'))['api']
On app/controllers/concerns/error_handling.rb define a reusable method to render your API errors in JSON format:
module ErrorHandling
def respond_with_error(error, invalid_resource = nil)
error = API_ERRORS[error]
error['details'] = invalid_resource.errors.full_messages if invalid_resource
render json: error, status: error['status']
end
end
On your API base controller include the concern so it's available on all the controllers which inherit from it:
include ErrorHandling
You will then be able to use your method on any of those controllers:
respond_with_error('not_found') # For standard API errors
respond_with_error('invalid_resource', @user) # For invalid resources
For example on your users controller you may have the following:
def create
if @user.save(your_api_params)
# Do whatever your API needs to do
else
respond_with_error('invalid_resource', @user)
end
end
The errors your API will output will look like this:
# For invalid resources
{
"code": "1",
"status": "400",
"title": "Bad Request",
"details": [
"Email format is incorrect"
]
}
# For standard API errors
{
"code": "2",
"status": "404",
"title": "Not Found",
"details": "Route not found."
}
As your API grows, you'll be able to easily add new error codes on your YAML file and use them with this method avoiding duplication and making your error codes consistent across your API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With