Whose Params Are They Anyway? Using the Params Hash in Rails
Tags: rails, beginners, codenewbie, tutorial
Published: 2022-12-13 21:33:26

_**Note:** This post discusses the params hash in the context of Ruby on Rails, but the essential details also apply if you're using Sinatra for your Ruby API._

In learning about writing server APIs and setting up backend routing over the last few weeks, one of the trickiest concepts to pin down was the params hash. In dynamic routing, this special little data structure somewhat mysteriously performs a dual role in accessing information from incoming requests. Like most things I've encountered in programming that behave differently in different contexts, I took note of the params hash as a potential source of confusion for new programmers like myself (or simply anyone new to Ruby APIs).

To hopefully alleviate or avoid such confusion, let's look at what the params hash does and how to use it for handling different types of client requests.

###What Even Is This?
First off, of course: what is the params hash? As the name tells us, it's a **hash** (a collection data structure with key-value pairs) that contains certain **parameters**. What exactly those parameters are and where they come from is what varies from case to case, and thus where confusion is likely to arise.

Broadly speaking, the params hash contains information included with HTTP requests. Depending on the type of request, it may include **dynamic path information** from the request URL, information from the **request body**, or **both**.

###Using the Params Hash with Dynamic Routes
We see the first and plainest of the params hash's two faces when we write dynamic backend routes like this:

```Ruby
# in config/routes.rb
get '/dinguses/:id', to 'dinguses#show'
```
This is one of the basic RESTful routes most Rails APIs will have for typical CRUD operations. In this case, a request to this route should return a single record from our database for the dingus resource matching a specified `id`. So if our API receives a `GET` request to `/dinguses/3`, it should return the dingus from our database with an `id` of 3.

The params hash is how our API accesses the actual value provided for that `id` in a given request. Specifically, we can refer to the params hash in the appropriate controller to get this value:

```Ruby
class DingusesController < ApplicationController
  def show
    render json: Dingus.find(params[:id]), status: :ok
  end
end
```
When our API receives that request to `/dinguses/3` and runs this controller action, the params hash contains the value of the dynamic part of the route indicated by the symbol `:id` as a key-value pair. The symbol is just a placeholder; the actual value for that placeholder from the request becomes the value, while the symbol becomes the key: `{id: '3'}`. That's why we can access it with bracket notation in our controller's `#show` method.

This works the same way in other methods for accessing a particular record by its `id`, like `#update` (for `PATCH` requests) and `#destroy` (for `DELETE` requests).

###Using the Params Hash with Request Bodies
The other face of the params hash is revealed when our API receives a request with information in its body, like a `POST` or `PATCH` request.

Say our frontend has a form so a user can create a new dingus and add it to our database. When they submit that form, the frontend performs a `fetch` request like this:

```JavaScript
fetch("https://www.dingusdomain.com/dinguses", {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({
    name: "Spade's Obsession",
    magnitude: 8
  })
})
```
The body of that `POST` request contains the information our API needs to create a new instance of the `Dingus` model and save it to the database. Conveniently, that information will end up as tidy key-value pairs in the params hash, which we can access in our controller:

```Ruby
# in config/routes.rb
post '/dinguses', to 'dinguses#create'

# in DingusesController
def create
  render json: Dingus.create(
    name: params[:name],
    magnitude: params[:magnitude]
  ),
  status: :created
end
```
_**Note:** This very simplistic way of writing this controller action does work, but we're only using it for the sake of example. It's much better to write a private method for **strong params** and use **mass assignment** in actions like `#create`... but that's a topic for another time._

###Double Your Params, Double Your Fun
Finally, the params hash can provide _both_ dynamic route information and request body information in the case of a `PATCH` request.

Consider the following:

```Ruby
# in config/routes.rb
patch '/dinguses/:id', to 'dinguses#update'

# in DingusesController
def update
  dingus = Dingus.find(params[:id])
  dingus.update(magnitude: params[:magnitude])
  render json: dingus, status: :accepted
end
```
Here we have a dynamic endpoint for requests to update a specific dingus. Like the example `#show` method above, the `#update` method in our controller gets the actual value of `:id` from the params hash and uses it to retrieve the matching record from the database. Then it _also_ accesses the params hash to get information from the body of the `PATCH` request (in this case just changing the value of the `magnitude` attribute).

###Params Demystified
The params hash has even more uses than what we've outlined here (like containing **query string parameters**), but these two cases are the most basic. Knowing how it carries dynamic route information and request body information, and how to access each kind in your controller actions, is fundamental to implementing typical RESTful routing in a Rails API. With those two essential uses clarified, hopefully this multivarious little data collection is a bit less of a mystery.