Rails API elegant error handling — The centralized way

Robin Seo
3 min readMar 19, 2022

--

Photo by Tai Bui on Unsplash

Rails application raise error on various context. for example, ActiveRecord find method raise ActiveRecord::RecordNotFound when it couldn’t find record with given condition.

Rails application has built-in error handler which is in Rack middleware. In case of ActiveRecord::RecordNotFound, Rails API application respond with error described on below.

Since the rails always ready to handle the error, You can focus on write your business logic and feel free from rescue error. This article introduces several ways to handle error in centralized way from easy way to elegant way.

Easy Way — Just leave the error raised

You can simply let your logic raise an error if it a response is enough from Rack middleware. You can write the controller like following.

Good Way — Let ApplicationController handle specific exceptions

ActionController provides rescue_from method which handle errors raised from the controller. Here is the example from Rails API document.

https://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html

It’s good enough, but the modular approach is better way since ApplicationController may have bunch of stuffs to do.

Better Way — Let the module handle it

Let’s create the module for ApplicationController to handle errors. First of all, define the spec of error response.

Let’s write the module. This module has Handler and several error classes. Handler has logics for rescuing error written in above.

lib/api_exception.rb

Let the application autoload classes and modules in lib directory.

app/config/application.rb

Finally, include ApiException::Handler module in ApplicationController. Every error in controllers extend ApplicationController would be handled by ApiException::Handler

Best Way — Let’s make an advance in ApiException Module

Let’s take a closer look at the way above.

The Problem — It has a difficulty to handle rails built-in exceptions

Rails built-in exceptions (e.g. ActiveRecord::RecordNotFound) would not respond with our spec. Following is a simple way to overcome.

With this approach, you always re-raise error on every built-in errors and even difficult to use messages in built-in error. Instead this, let the module handle both your custom error and built-in error.

Note that you can see the list of built-in errors handled by Rack with command below.

$ rails r "pp ActionDispatch::ExceptionWrapper.rescue_responses"

The problem — Bad readability

You would write 8-line codes to define error class which has deadly simple information.

To improve readability, let describe error information in hash.

Thanks to ruby is dynamic language, you can define class on runtime. Let ApiException::Handler find or create the error class by the name before executing rescue_from method.

Improvement — Additional slot for detail message

Built-in error throw additional information sometimes. for example, ActiveRecord validator raise ActiveRecord::RecordInvalid with detailed message.

Post.create! 
>> ActiveRecord::RecordInvalid (Validation failed: Title can't be blank)

You can even raise your own error with additional message.

raise StandardError, "My awesome error message" 
>> StandardError (My awesome error message)

Let the handler respond with detailed message in detail field if raised error has an additional message.

The complete code is following. the toy project for this article in here

--

--