How to use Rails Strong Parameters

In the latest major version of Ruby on Rails, Strong Parameters were introduced. The intent of this addition was to enable consistent and reliable parameter checking. Using Strong Parameters is simple and intuitive. It provides a very clean method API to help keep controllers DRY.

However, knowing when to use Strong Parameters and how to use them correctly is very important. After all, what good is parameter validation if it is in the wrong place or conveys the wrong message?

Hit the Books

The trusty “booksandreviews.com” will serve as a good example application.

Given a controller that can create Authors, a default pattern without Strong Parameters could look like:

class AuthorsController < ApplicationController
  # POST /authors
  def create
    @author = Author.create!(author_params)

    redirect_to @author, notice: 'Author was successfully created.'
  end

  private

  def author_params
    params[:author]
  end
end

Assuming that there are no presence validations on the Author class itself (or in the form view creating this model), this code has no check to make sure that anything about an Author is passed in to create.

This means that any POST request is made to /authors will create a new Author. While this kind of problem would most likely not make it to production, let us assume that the poor people over at “booksandreviews.com” came from such humble code beginnings that this was the first iteration of the AuthorsController.

To make the AuthorsController more robust, the require method can be used on the params hash:

class AuthorsController < ApplicationController
  # ...

  private

  def author_params
    params.require(:author)
  end
end

Now if a POST to /authors does not contain a payload with an :author key in the body, the request will error:

ActionController::ParameterMissing

However, a request with the :author attribute will…

ActiveModel::ForbiddenAttributesError Also cause an error, apparently.

This is another feature of Strong Parameters. The reason an error occurs is that the author_params method did not specify which parameters could be mass assigned to the Author class. Passing the result of require directly to Author.create will trip this safety measure.

Shut ‘em Down

To correct the ForbiddenAttributesError and add some much needed structure to the AuthorsController, the permit method needs to be used in conjunction with require.

Adding a list of attributes as individual symbols will make sure that only those attributes can be passed through to Author.create!

class AuthorsController < ApplicationController
  # ...

  private

  def author_params
    params.require(:author).permit(:name, :country, :email)
  end
end

Great! Now a properly formatted POST containing only the correct attributes will create an Author appropriately.

But, why go through all the hassle? There should be a way to let all attributes pass, regardless of how crazy harmful they could be to our application we use to pay bills.

The permit! method is a completely permissive way to allow all attributes to be mass assigned to a model:

class AuthorsController < ApplicationController
  # ...

  private

  def author_params
    params.require(:author).permit!
  end
end

This will allow any attributes nested under :author to be set on an Author. A solution like this requires no declaration of attributes, but is this better?

The short answer is “no”.

The reason why being completely permissive with parameters is a bad idea is because not all attributes were created equally.

If “booksandreviews.com” had an admin or superuser column on their authors table, any POST with admin: true would create not a normal Author, but an all-mighty Author.

Specifying the attributes that can be mass assigned to an ActiveRecord model is not a new concept, with Strong Parameters this pattern is just too easy not to do.

Open Up Shop

Another great feature of using Strong Parameters is type assertion. Regardless of ActiveRecord’s type coercion, ensuring that data is as valid as early as possible is important.

Consider that an Author can have an array of genres which summarize their bodies of work. To ensure that this list of genres is an array, a hash syntax can be used:

class AuthorsController < ApplicationController
  # ...

  private

  def author_params
    params.require(:author)
          .permit(:name, :country, :email, { genres: [] })
  end
end

By adding { genres: [] } to the end of the permit call, it ensures that the genres value is always an array. This prevents random nonsense or a malformed request from compromising the Author class’ data integrity. If something other than an array is passed as the genres attribute, it will disregard it.

Alternatively, when a parameter is a hash (like preferences), all the keys in the hash need to be present in the permit call.

{
  "preferences": {
    "time_zone": "GMT",
    "subscribed": true
  }
}
class AuthorsController < ApplicationController
  # ...

  private

  def author_params
    params.require(:author)
          .permit(:name,
                  :country,
                  :email,
                  { preferences: [:time_zone, :subscribed] })
  end
end

If this is a tad confusing, check out more examples at the official guide for Strong Parameters.

Everything in Moderation

Strong Parameters is not appropriate for every situation. In the case that a certain number of parameters must be present, or if one parameter must only be present when not accompanied by another specific parameter, Strong Parameters will fall short. The time and place to use Strong Parameters is when creating or updating an ActiveRecord object.

Additionally, if the Ruby on Rails application in question was an API, Strong Parameters would only be effective in very specific circumstances. Since the feedback given by Strong Parameters is not as granular as an API would like to be, it could be hard to find many uses of it.