18 July 2020
Welcome to the fifth chapter of the API-only ruby on rails course. In this chapter, we will introduce the Renderer
module for the API controllers. If you haven’t read the previous chapters yet, you can check the content list from the course page.
As you remember from the previous chapters, we have a user creation endpoint as following.
Even though we only have one action at the moment, we will have the same behavior for other endpoints. If the desired condition is done successfully, we would like to render the JSON representation of the resource along with the HTTP status code. Otherwise, we want to render the JSON representation of the errors for the resource. If the endpoint has only one possible path like getting the resource information, we will again use the first approach.
Instead of writing render json: @user, status: :created
and render json: @user.errors, status: :unprocessable_entity
every time in the controllers, we can extract those code blocks to the specific methods called render_object
and render_errors
. We can then place them to a separate module to be able to use from different API controllers and have a single place to structure all API payloads. It will also make it easy to add metadata information for the payloads, which we will cover in the next chapter.
We will build the renderer
module while refactoring the create
action of the users controller, but we will do it with small iterations and check the tests to feel confident enough about our changes.
Let’s start with creating a concern file (app/controllers/concerns/renderer.rb
) and implement render_object
method as a first step.
With the render_object
method, we basically get the resource object and render them as we were doing in the users controller. We also used :ok
status as a default parameter, since most of the API responses will return HTTP 200 status code. Now let’s use the render_object
method from the users controller. (Do not forget to include renderer
module!)
Now, we can run user request spec to see if everything still works as before.
Since everything works as expected, we can do the same enhancement with the render_errors
method.
Here we passed unprocessable_entity
as a default value of the status parameter, which is 422 HTTP status code. And again, let’s refactor the create
action of the users controller.
And again, let’s run user request spec to see tests are still green.
All tests are passed, perfect! As you can see, we refactor our create
action confidently since we have proper request tests. As a general rule of thumb, it’s good to check whether you have corresponding specs before refactoring any place in the codebase.
We included the renderer
module inside of the users controller, but we need to move it to the base controller because we will use that functionality also from other API controllers, which are extended by the base controller.
We already checked requests specs for the user controller but we also need to write necessary tests for the renderer module itself. Let’s create spec/concerns/renderer_spec.rb
file and start with writing spec for the render_object
method.
Here, we created an anonymous rails controller extended by ApplicationController
and included the Renderer
module. Then we built a show action that uses the render_object
method with the user resource. And later, we defined the controller spec for the anonymous controller we created, and we sent a request to check if the render_object
method returns the expected resource payload along with the correct HTTP status code.
Now we can extend same approach for the render_errors
method.
We tried to create a user record with invalid parameters with the create action, and then we returned validation errors with the render_errors
method. Similar to the render_object
test, we send a request to create
action and checked if it has the correct payload with the status code.
Let’s run renderer specs to see everything is working as intended.
You might have realized that we have a Metrics/BlockLength
rubocop offense for renderer_spec.rb
file since rspec block has too many lines. But since it’s actually the DSL of the rspec, I want to exclude spec directory from this rule.
Before finishing the chapter, let’s run all specs to make sure we didn’t break any existing behavior of the application.
In this chapter, we introduced the renderer
module and refactored the create action of users controller by using render_object
and render_errors
methods. In the next chapter, we will improve the renderer
module to have a better API payload structure, and we will add metadata information to the API responses. You can find the source code of bookmarker application on github. You can also find the previous chapter here.
Thank you for reading! If you want to be notified when I publish a new chapter, you can follow me on twitter.