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'
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
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
})
})
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
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
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.
Top comments (0)