/ ruby

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.

Manu S Ajith

Manu S Ajith

Tech Entrepreneur, dating Elixir, in long-term ❤️ w/ Ruby, had multiple one night stands w/ Go. Into functional paradigms DDD/CQRS/EventSourcing architecture these days. @manusajith on the interwebs

Read More