Progress isn’t made by early risers. It’s made by lazy men trying to find easier ways to do something ― Robert Heinlein
There are many issues that developers face and have to overcome during developing large systems. Here at MdG, it is a daily experience and besides solving them, we try to learn from these complexities
Usually, when you are developing an API there are different features that you have to implement like Authentication, File Uploads, Background Jobs, etc. But there is also boilerplate code and repetition in most RESful controllers.
We decided to come up with a better way of writing RESTful controllers.

When your web app receives a request, the router will decide which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action. For example, if the user visits /posts/new
inside the web application, Rails will create an instance of PostsController
and calls its new
method.
As we know from inheritance in Ruby if you call a method on the object, Ruby looks for the method on the class of that object, next it looks for the method in one of the parent classes, if not found Ruby will start another search from the object and tries to locate method_missing
, and if method_missing
is not found It will throw a NoMethodError
.
So we took advantage of Ruby inheritance lookup on our controllers.
We decided to have a Base API Controller
which implements all the RESTful actions, and other RESTful controllers can inherit from it.

Using the right ActiveRecord model insideBaseApiController
actions
If child controllers followed the Rails convention each of them should have a model with the same name as theirs, but in singular form(ex. for PostsController.rb
it should be a Post
).
First, we needed the name of the controller that was currently using the actions of the BaseApiController
. In Rails, every controller extends ApplicationController
but the real functionality resides inside ActionController:: Base
. So in Rails docs about ActionController:: Base
there, we found some useful methods.

Inside ourBaseApiController.rb
we callcontroller_name
(provided by ActionController
) to get the name of the current child controller and chain singularize
to make it singular.
The final result is a Stringpost
:
[1, 9] in /media/azdren/AZDREN/rails-projects/blog-app/app/controllers/base_api_controller.rb
1: # frozen_string_literal: true
2:
3: class BaseApiController < ApplicationController
4: # GET /posts/hello
5: def hello
6: byebug
=> 7: render plain: 'Hello there'
8: end
9: end
(byebug) self.controller_name
=> "posts"
(byebug) self.controller_name.singularize
=> "post"
Next, we chain another method classify
( ActiveSupport
)which internally is used by Rails to convert plural string table names into singular models. Finally, to convert it into a class we call constantize
(ActiveSupport
) which tries to find a constant with a given name.
Result:
[1, 9] in /media/azdren/AZDREN/rails-projects/blog-app/app/controllers/base_api_controller.rb
1: # frozen_string_literal: true
2:
3: class BaseApiController < ApplicationController
4: # GET /posts/hello
5: def hello
6: byebug
=> 7: render plain: 'Hello there'
8: end
9: end
(byebug) self.controller_name.classify.constantize
=> Post(id: integer, created_at: datetime, updated_at: datetime)
The final code listing:
Applying a before_action
filter
On line 4 we apply a before_action
filter which makes sure that before running any of the actions we create our model class based on the controller name.
# frozen_string_literal: true
class BaseApiController < ApplicationController
before_action :class_model
class_model
method:
def class_model
@class_model ||= res_name.classify.constantize
end
res_name
method:
def res_name
@res_name ||= self.controller_name.singularize
end
Index action
Inside index
action, we call @class_model.all
and store the result on data
variable. Next render_json_data
is called which sets the data to an instance variable named after model and renders it as JSON.
def index
data = @class_model.all # similar to User.all or Post.all
render_json_data(data)
end
render_json_data
method:
def render_json_data(data)
render json: instance_variable_set("@#{res_name.pluralize}", data)
Show action
On the show
action instead of getting all the data we look for one record provided by the id
parameter :
def show
data = @class_model.find(params[:id])
render_json_data(data)
end
Create action
Create action
is a bit more complex. First, we call set_res
and inside it, we create an instance of the model with res_params
passed on the constructor. Next set_res
checks if the argument is nil
and if it is, it runs another query that tries to get the model based on its id
parameter. In the end, it assigns the data to a new instance variable with the name of the model. Finally, we call created_res.save
which saves the data. If the model gets saved we render it otherwise we render its errors.
def create
set_res(class_model.new(res_params))
if created_res.save
render_json_data(created_res)
else
render_json_data(created_res.errors.full_messages)
end
end
set_res
method:
def set_res(res = nil)
res ||= class_model.find(params[:id]) # Post.find(params[:id])
instance_variable_set("@#{res_name}", res)
end
created_res
method:
def created_res
instance_variable_get("@#{res_name}")
end
res_params
method:
def res_params
@res_params ||= self.send("#{res_name}_params")
end
Update action
This action is kind of similar to create
action with a small difference. Instead of trying to save the object, we call the update
method:
def update
set_res(class_model.find(params[:id]))
if created_res.update(res_params)
render_json_data(created_res)
else
render_json_data(created_res.errors.full_messages)
end
end
Destroy action
Same thing with destroy
action instead of saving we delete the data and return a response:
def destroy
set_res(class_model.find(params[:id]))
if created_res.delete
render_json_data({ message: 'Successfuly deleted', status: 200 })
else
render_json_data(created_res.errors.full_messages)
end
end
Github Repo:
github.com/montedelgallo/base_api_controller
Resources:
Action Controller Overview, Ruby Inheritance, ActionController::Base, controller_name, singularize, classify, constantize, Generic Rails Controllers.