Error handling with Grape, Rails and ActiveRecord CanCan
I have been working on a project developing the backend API using Rails and Grape. One fine morning I pulled the latest code from the git repo that my colleague wrote, and was going to review the code. As usual, the first thing I did was to run the Integrations specs and to my surprise, some of the specs were failing.
The failing specs were mostly around the Sad path, and looking deeper I realized that the exceptions were not being handled properly in the code and was causing the specs to fail.
So, given the scenario, I wanted to handle the exceptions generated by the Grape API in a much clean and DRY way as these exceptions are ought to happen in the API any time.
So I decided to write a concern, by extending the ActiveSupport::Concern to handle all the exceptions that the app generates. So here is an abstract of the code that I came up with.
module API
module V1
module ExceptionsHandler
extend ActiveSupport::Concern
included do
rescue_from :all do |e|
# When required params are missing or validation fails
if e.class.name == 'Grape::Exceptions::ValidationErrors'
error!(e.message, status: 406)
# Bad token
elsif e.class.name == 'RuntimeError' && e.message == 'Invalid base64 string'
error!('401 Unauthorized', status: 401)
# AccessDenied - Authorization failure
elsif e.class.name == 'CanCan::AccessDenied'
error!('You don\'t have permissions.', status: 403)
# Record not found
elsif e.class.name == ActiveRecord::RecordNotFound do |e|
error!(e.message, status: 404)
# When all hell broke loose
else
Rails.logger.error "\n#{e.class.name} (#{e.message}):"
e.backtrace.each { |line| Rails.logger.error line }
Rack::Response.new({
error: '400 Bad Request',
errors: e.errors
}.to_json, 400)
end
end
end
end
end
end
You can add your own custom exceptions to the case. Some of the exceptions that Grape supports can be found here
Hope someone finds this helpful.