Make ActiveRecord::Enum validation in Rails API application
As all know, Before Rails version 7.1 When you assign an incorrect value to any record that has an enum as type, it normally raises an ArgumentError
as Server Error status: 500
And, for those who do know, a Rails enum is a way to define a set of allowed values for an attribute in a Rails model. Enums are stored as integers in the database but can be accessed and used in Ruby as symbols.
Enums were added to Rails in the 4.1 version, but until Rails 7.1, the ability to validate the enums was missing.
Let’s explore a practical scenario using a User model in a Rails API application. Consider the User model with various attributes, including a age_group
column defined as an Enum which is expected to contain only the values specified here in my user.rb file:
# user.rb
class User < ApplicationRecord
...
enum age_group: { infants: 0, children: 1, adolescents: 2, adults: 3 }.freeze
end
If an incorrect value is assigned to the age_group
column, Rails would raise an ArgumentError
as shown below:
After doing more research to find a better way to catch and display a correct enum error, I found different approaches to doing so, but I felt they required more code logic to write than I wanted to find my own way to handle this more gracefully, and here it is.
Catching Enum Errors
In the Api::UsersController
, we can implement a rescue block to catch the ArgumentError
and provide a more user-friendly response; it is just simple. 😄
First, go to your controller in the create or update action:
# users_controller.rb
class Api::UsersController < ApplicationController
before_action :set_user, only: %i[show update destroy]
...
# PATCH/PUT /users/1
def update
if @user.update(user_params)
render_success_response('Profile updated successfully', @user)
else
render_unprocessable_entity_response(@user.errors)
end
rescue ArgumentError => e
render_unprocessable_entity_response(e.message)
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound => e
render_not_found_response(e.message)
end
# Only allow a list of trusted parameters through.
def user_params
params.require(:user).permit(:username, :age_group, :remember_me, :terms_of_service)
end
end
The update action in `UsersController` handles profile modifications. Upon a successful update, it renders a successful response. If there are validation errors, it invokes `render_unprocessable_entity_response` with user errors. A rescue block addresses `ArgumentError` during updates, ensuring consistent feedback for enum-related errors.
Response Helper Module
Then, to maintain consistency in handling responses, a ResponseHelper
module is created in app/controllers/concerns/response_helper.rb
. This module includes methods for rendering success, unauthorized, not found, and unprocessable entity responses:
# response_helper.rb
module ResponseHelper
def render_success_response(message, data)
render json: {
success: true,
message: message,
data: data
}, status: :ok
end
# unprocessable_entity
def render_unprocessable_entity_response(error = 'Unprocessable entity')
render json: {
success: false,
errors: error,
}, status: :unprocessable_entity
end
def render_unauthorized_response(message = 'Unauthorized')
...
end
def render_not_found_response(message)
render json: {
success: false,
message: message,
data: nil
}, status: :not_found
end
end
Don’t forget to include the ResponseHelper
module in your app/controllers/application_controller.rb
:
# application_controller.rb
class ApplicationController < ActionController::API
include ResponseHelper
# Other configurations...
end
With these enhancements, your Rails API application will handle Enum errors more gracefully, providing clear and consistent responses to clients.
Written by Ben Mukebo.
I am a software developer, familiar with a variety of different web technologies and frameworks, and keen on always finding ways to explain things as simple as possible.
If this article has been helpful, please hit the 👏 button and share it with others! 🚀 to show how much you liked it! 😄