Whose Params Are They Anyway? Using the Params Hash in Rails

souzamartin

Martin Souza

Posted on December 13, 2022

Whose Params Are They Anyway? Using the Params Hash in Rails

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:

# in config/routes.rb
get '/dinguses/:id', to 'dinguses#show'
Enter fullscreen mode Exit fullscreen mode

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:

class DingusesController < ApplicationController
  def show
    render json: Dingus.find(params[:id]), status: :ok
  end
end
Enter fullscreen mode Exit fullscreen mode

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:

fetch("https://www.dingusdomain.com/dinguses", {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({
    name: "Spade's Obsession",
    magnitude: 8
  })
})
Enter fullscreen mode Exit fullscreen mode

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:

# 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
Enter fullscreen mode Exit fullscreen mode

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:

# 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
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
souzamartin
Martin Souza

Posted on December 13, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related