DEV Community

AlexMcD-git
AlexMcD-git

Posted on

Serializers for your Serializers

The last few weeks I was learning ruby on rails and was especially impressed by the powerful simplicity of serializers. For my example, I build an app for tracking experiments and chemical inventory for a lab group. Users can log in and create experiments and attach or remove chemicals to an experiment. Chemicals can also be created in the DB, have their amount edited, and be removed. For example, I can create an experiment with the description "Baking soda and vinegar volcano". For this I can add the existing chemicals, baking soda, and vinegar. Important to note that these chemicals could be part of multiple experiments at once, so the models are connected with a join table like so:

#/experiment.rb
class Experiment < ApplicationRecord
    has_many :chemical_experiments, dependent: :destroy
    has_many :chemicals, through: :chemical_experiments
    belongs_to :user

#/chemical.rb
class Chemical < ApplicationRecord
    has_many :chemical_experiments, dependent: :destroy
    has_many :experiments, through: :chemical_experiments

#/chemical_experiment.rb
class ChemicalExperiment < ApplicationRecord
  belongs_to :chemical
  belongs_to :experiment

#/user.rb
class User < ApplicationRecord
    has_many :experiments
Enter fullscreen mode Exit fullscreen mode

If I log out and then back in, I want to be able to fetch all of the user's data in one request, so that I can make use of React's responsive rendering with state. If set up the same :belongs_to and :has_many for user, experiments, and chemicals (the join table and through: is not needed in serializer if the models are set up correctly).

#/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :username
  has_many :experiments

#/experiment_serializer.rb
class ExperimentSerializer < ActiveModel::Serializer
  attributes :id, :description, :is_complete
  has_many :chemicals

#/chemical_serializer.rb
class ChemicalSerializer < ActiveModel::Serializer
  attributes :id, :name, :amount_in_grams, :location
Enter fullscreen mode Exit fullscreen mode

So, our user#show method (GET /user/:id) should return a user object with id, username, and an array of experiments. Each experiment object should return it's attributes and an array of chemical objects.

#/users_controller.rb
    def show
        user = User.find(params[:id])
        render json: current_user, status: 200
    end
Enter fullscreen mode Exit fullscreen mode

Fetching user with id of 1 would return something like this:

{
  "id": 1,
  "username": "Alex",
  "experiments": [
    {
      "id": 1,
      "description": "Baking soda and vinegar volcano",
      "is_complete": false
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The problem is that, by default, ActiveModel Serializer only will return one layer deep. In order to return the next layer, chemicals associated with the experiment, we need to make a slight change in our user_controller.rb.

#/users_controller.rb
    def show
        user = User.find(params[:id])
        render json: current_user, include: ['experiments', 'experiments.chemicals'], status: 200
    end
Enter fullscreen mode Exit fullscreen mode

with this slight change we have just a few lines of code split between a few files that builds our tree of data. What the fetch request should now return is the user with all of the nested data that we built serializers for.

{
  "id": 1,
  "username": "Alex",
  "experiments": [
    {
      "id": 1,
      "description": "Baking soda and vinegar volcano",
      "is_complete": false,
      "chemicals": [
          {
              "id": 1,
              "name": "Acetic Acid",
              "amount_in_grams": 500,
              "location": "Acids Cabinet, Lab B"
          },
          {
              "id": 2,
              "name": "Sodium Bicarbonate",
              "amount_in_grams": 1000,
              "location": "Chemical Storage, Shelf 2"
          }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This nicely structured data will make building out the front end much easier, especially in a framework like react. I hope other developers taking their first delve into ruby on rails will find some of this information useful.

Discussion (0)