How the Rails params hash works
Tags: rails, ruby
Published: 2024-03-20 07:00:00

*This article was originally written by [Jeffery Morhous](https://www.honeybadger.io/blog/authors/jefferymorhous/) on the [Honeybadger Developer Blog](https://www.honeybadger.io/blog/how-the-rails-params-hash-works/).*

In Ruby on Rails development, understanding the functionality and role of the params hash is imperative. It serves as a conduit for incoming HTTP request data, connecting user interface elements to the underlying application logic.

In this article, we'll explore the params hash, including its inner workings and practical applications within Rails projects. Additionally, we will discuss the advent of strong parameters, a security measure integrated into Rails to safeguard against
malicious user input.

## HTTP requests, parameters, and introduction to the params hash in Rails

Applications send and receive a wealth of information each time a user visits a webpage or submits a form. How does this information travel, and where does it end up once it reaches our Rails application? The answer is through Hypertext Transfer Protocol (HTTP) requests and, more specifically, through parameters embedded within these requests.

HTTP is the backbone of data communication on the internet. It defines the format for sending and receiving messages between clients and servers. When you fill out a form on a webpage and hit 'Submit', your browser sends an HTTP request to the server, complete with parameters that contain the data you entered into the form.

In Rails, these __incoming parameters__ find a tidy home in the [params hash](https://api.rubyonrails.org/v7.0.4.2/classes/ActionController/Parameters.html). This hash is automatically made available in your Rails controllers, giving you easy access to all the parameters that came with the request. It doesn't matter whether the
parameters were tucked into the URL, came as part of a form submission, or even if they were sent as part of a JSON request body; Rails bundles them all into the params hash.

Controller methods can take advantage of the params hash to get user-provided values in a clean format:

```ruby
def create
  @user = User.new(params[:user])
  # Rest of the controller action...
end
```

The params hash is a versatile tool that Rails developers should be comfortable using. Next, let's dive a little deeper into the params hash and see how we can make the most of it.

## Structure of the params hash

To fully wield the power of the params hash, we first need to understand its structure. In Rails, the params hash is not just a plain old Ruby hash. Instead, it's an instance of ActionController::Parameters, a subclass of HashWithIndifferentAccess. This special kind of hash allows you to access its values using either symbols or strings as keys.

Both of the following accesses are valid:

```ruby
params[:id]
params['id']
```

The params hash usually contains several key-value pairs. Some of these are added by Rails itself, such as :controller and :action, which tell you which controller and action are processing the request. Any other parameters sent with the request, whether
in the URL or the request body, also find their way into the params hash.

When dealing with nested parameters, as you often will when working with forms, the params hash can get a little more complex. For instance, consider a form submission for creating a new user. The form might include fields for a nested profile object. The parameters for this request could look something like this:

```ruby

{ "controller" => "users", 
  "action" => "create",
  "user" => {
    "username" => "jeffmorhous", 
    "email" => "jeff@example.com",
    "attributes" => {
      "location" => "San Francisco"
    }
  }
}
```

However, we can still pull out the specific data needed:

```ruby
params[:user][:username]
params[:user][:attributes][:location]
```

Having a good handle on the structure of the params hash will make your work in Rails controllers smoother and more efficient. In the next section, we'll look at how Rails treats different kinds of parameters.

## URL params vs. query params

It's important to understand the two main types of parameters we'll encounter: URL parameters and query parameters. Although Rails combines both types into the params hash, they originate from different parts of an incoming HTTP request.

URL parameters, also known as path parameters, are embedded directly into the URL. In Rails, you often use these to specify the ID of a resource with which you want to interact. Take the following URL for example: `http://www.example.com/users/25`. The '25'
here is a URL parameter, representing the ID of the user we want to show.

However, query parameters are included after a question mark (?) in the URL and formatted as key-value pairs. These are often used for filtering or sorting data. For instance, `http://www.example.com/users?location=San+Francisco` includes a
query parameter of `location`, which has the value `San+Francisco`.

Regardless of whether a parameter comes from the URL or the query string, Rails puts it in the params hash for you to access in your controller. No matter the origin, all parameters end up
in the same destination: the Rails params hash. The type of parameter simply changes where the data comes from in the HTTP request and how the data is formatted in the request.

## Accessing data in the params hash

One of the main tasks you'll perform with params is extracting data to use within your controller actions. We've already looked at a few examples, so let's recap.

Accessing data from the params hash is as simple as treating it like any other Ruby hash. You can use either string or symbol keys to get the value you want. Let's say we have a params hash like the following: `{ "controller" => "users", "action" => "show", "id" => "2" }`. We could get the 'id' parameter with either of the following lines:

```ruby
params[:id]       # => '2'
params['id']      # => '2'
```

Remember the nested parameters example from earlier? Extracting data from a nested structure is a little more involved but follows the same principles. You can chain keys to drill down into the hash:

```ruby
params[:user][:username]
params[:user][:attributes][:location]
```

It's important to remember that if you try to access a key that doesn't exist in `params`, Rails will return nil rather than raising an error. In the next section, we'll talk about strong parameters, a feature that can help protect your application from malicious user input.

## Strong parameters

When we're dealing with user input, there's always a risk of malicious activity. The introduction of [strong parameters](https://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html) in Rails 4 provided a way to secure our applications by controlling which parameters are permitted in our controllers.

In the past, Rails used a feature called 'attribute protection' to guard against unwanted parameters. However, this feature was model-based, which could lead to bloated models and a lack of flexibility. Strong parameters shifted this protection to the
controller, where it makes the most sense. This way, we can handle user input at the point of contact, before it reaches our models.

The key method introduced by strong parameters is `require`. This method ensures that a specific parameter is present, or an ActionController::ParameterMissing error is raised. We can chain another method, `permit`, on a `require` to specify which nested parameters are allowed:

```ruby
params.require(:user).permit(:username, :email, attributes: [:location])
```

In this example, we're saying that the :user parameter must be present. Within :user, only the `:username`, `:email`, and `:attributes` keys are allowed. Furthermore, within
`:attributes`, only `:location` is permitted.

It's common practice to put this `params.require` call into a private method, and then use the result of calling this method to access params. The method might look like this:

```ruby
private

  def user_params
    params.require(:user).permit(:username, :email, attributes: [:location])
  end
```

Meanwhile, instead of using `params` to access the params hash, you could instead use `user_params`:

```ruby
user_params[:user][:username]
user_params[:user][:attributes][:location]
```

Strong parameters give us the ability to define what's allowed and what's not in a clear and concise way. This makes it an essential part of managing the params hash in a secure Rails application.

## Conclusion

As we've seen throughout this exploration, the params hash is a powerful and flexible feature in Rails. It provides a simple interface for accessing data from incoming HTTP requests, whether it's URL parameters, form data, or even API request bodies. By understanding how parameters work, you can handle user input in a secure and efficient manner.

The advent of strong parameters added a layer of security to this process, ensuring that Rails applications are protected from unwanted data manipulation. Through the appropriate use of require and permit methods, we can filter out unwanted parameters and maintain the integrity of our data.

While the params hash might seem like just another part of Rails,
it plays a key role in making your application functional and secure. Deepening your understanding of it empowers you to make full use of its capabilities in your Rails applications.