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:

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)