JSON is a favorite format for exchanging data between servers and clients among developers. Rails provide support for rendering proper JSON response, making it easy to focus on the core functionality of the application. Here are a few things I learned to create a clean, customized Rails JSON render response.
Suppose we have a User model with fields such as first_name, last_name, picture, age, authentication_token, password_digest and timestamps fields such as created_at and updated_at. Using Rails render json: @user will convert the user object to json by calling “to_json” method of the user for you.
render json: @user
The generated rails json response response contains all fields of our User model, including authentication tokens, password digest, timestamps or other sensitive information related to a user that should not be permitted in response.
To prevent such information from being sent I used to prepare customized JSON responses in Rails by logic related to rendering inside of helper classes and controller actions. That Rails controller JSON return response approach just increased redundancy, inconsistency, inefficiency and was harder to maintain. Luckily, soon I got introduced to the concept of overriding “as_json” method.
# override as_json method of User class
def as_json(options={})
super(only: [:picture, :age],
methods: [:name])
end
# put helper methods, such as creating full name from given first_name and last_name attribute of user
def name
self.first_name +" " +self.last_name
end
# example response, note that timestamps, authentication token and password digest fields are not present
# user: {
# name: "some name",
# picture: "url",
# age: 20
# }
# most important: calling render json: @user returns this response
This approach seems clean and nice. We are now showing only name, picture and age fields. But this is not enough.
Suppose, our user can have many emails. Now with user response, we need to send data about emails that a user owns. We don’t want to send timestamps fields of any models. Also, we want to send emails information in only certain requests where they are required. Such customization can be done by using the options hash and passing which fields we want in our response.
def as_json(options={})
# options hash accepts four keys for better customization :only, :methods, :include, :except
# so whenever such keys are found, we call super with those keys to provide response consisting only those keys
if options.key?(:only) or options.key?(:methods) or options.key?(:include) or options.key?(:except)
h = super(options)
else
h = super(only: [:picture, :age],
methods: [:name],
include: {:emails => { :only => [:id, :email] })
end
end
# response when no options passed:
# ex. render json: @user
# user: {
# picture: "url",
# age: "20",
# name: "some name",
# emails: [
# {
# id: 1,
# email: "somename@gmail.com"
# },
# {
# id: 2,
# email: "somename@yahoo.com"
# }
# ]
# }
# response when options passed
# ex: render json: @user.as_json(only: [:picture, :age], methods: [:name])
# user: {
# picture: "url",
# age: "20",
# name: "some name"
In the above example, by default when we call
render json: @user
It returns user hash with related email information. Note that only the required information is passed in response to JSON. When we need to exclude emails information from the response, we just need to pass fields we need in options hash as shown in the example as:
render json: @user.as_json(only: [:picture, :age], methods: [:name])
This is the approach I use generally to simplify Rails JSON render response and is very effective.
There is one better approach of using active model serializers which delegates as_json and to_json to adapters. I am learning how to use that in my projects as I came to know about it recently. So far active model serializers seem cleaner and more flexible approach than overriding as_json method and will post about that soon.
Thanks for reading, you can let me know about how you render JSON response in Ruby on Rails and what you can do to improve the process.
Originally posted on parthtalks by Parth Modi.