Rails 8 introduces `params.expect`

Updated . Posted . Visible to the public. Repeats.

The new params.expect method in Rails 8 improves parameter filtering, addressing issues with malformed input and enhancing security. It provides a cleaner, more explicit way to enforce the structure and types of incoming parameters.

What changed

  • Replaces require and permit: Combines both methods for concise parameter validation.
  • Explicit Array Handling: Requires double array syntax to define arrays of hashes, improving clarity.
  • Enhanced Validation: Ensures expected parameter structure, rejecting malformed input with a 400 error instead of a 500 Error.
    Invalid input structures raise ActionController::ParameterMissing rather than NoMethodError when a disallowed type (e.g., string instead of hash or array) is provided for the permitted attributes.

Example

Basic Usage

# Old before Rails 8
user_params = params.require(:user).permit(:name)

# New since Rails 8
user_params = params.expect(user: [:name])

Allowing Arrays

For an array of hashes:

# Params to allow: { "comments" => [ {"text" => "Hi!"}, {"id" => "Yep."} ] }
# For the url format comments[][text]=Hi!&comments[][id]=Yep.

params = ActionController::Parameters.new({ "comments" => 
  [ 
    {"text" => "Hi!"}, 
    {"text" => "Yep."}
  ]
})

params.expect(comments: [[:text]])
# => [#<ActionController::Parameters {"text" => "Hi!"} permitted: true>, #<ActionController::Parameters {"text" => "Yep."} permitted: true>]


# Params to allow: { "posts" => { id: 12, tags: [ { name: "card" }, { name: "blog" } ] } }
# For the url format posts[id]=12&posts[tags][][name]=blog&posts[tags][][name]=card

params = ActionController::Parameters.new({ "posts" => {
  id: 12,
  tags: [ 
    {"name" => "blog"}, 
    {"name" => "card"}
  ]
}})

params.expect(posts: [tags: [[:name]]])
# => #<ActionController::Parameters {"tags" => [#<ActionController::Parameters {"name" => "blog"} permitted: true>, #<ActionController::Parameters {"name" => "card"} permitted: true>]} permitted: true>

For an array of strings:

# Params to allow: { "posts" => { id: 12, tags: ["card", "blog"] } }
# For the url format posts[id]=12&posts[tags][]=blog&posts[tags][]=card

params = ActionController::Parameters.new({ "posts" => {
  id: 12,
  tags: [ 
    "blog", 
    "card"
  ]
}})

params.expect(posts: [:id, tags: []])
#<ActionController::Parameters {"id" => 12, "tags" => ["blog", "card"]} permitted: true>

Further resources

Martin Emde, the creator of the pull request Show archive.org snapshot wrote two great blog posts on the topic:

Using Strong Parameters

We also have the following two cards for more details and potential gotchas on using strong parameters:

Profile picture of Felix Eschey
Felix Eschey
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Felix Eschey to makandra dev (2025-01-20 06:15)