Strong params with a JS frontend and a Rails backend
I recently completed my JavaScript portfolio project at Flatiron. Coming from a purely Ruby / Rails background, this was my first ever time truly implementing JavaScript.
(I went in determined to fall in love with JS the same way I had with Ruby many months ago, and I think it’s safe to say that I did. Anything’s possible. It blows my mind.)
The thing I struggled with most was integrating the front endPOST
request with my backend create
method: specifically whitelisting my strong params. Coming from Rails, I had gotten used to Form Helpers taking care of the necessary behind the scenes legwork for me.
In a typical Rails app, a basic strong_params
controller method looks like this:
privatedef model_params
params.require(:model_name).permit(:attribute_1, :attribute_2)
end
When data is submitted using form_with
or form_for,
both of which are bound directly to the ActiveRecord models they were created for, the params
that come in already contain the required model_name
param above (because the form knows which model it belongs to).
But, when submitting a form from JS, this Rails magic is noticeably absent.
The app that I created is a (colorful) version of my favorite old school game: Brick. On GAME OVER, it allows the player to save their high score to the scoreboard. A User has_many
games, and a Game belongs_to
a user.
My games_controller strong param method was as follows:
def game_params
params.require(:game).permit(:score, :user_name)
end
And I created a custom setter/getter for user_name
so that a user could be instantiated alongside their score.
But…my games weren’t saving.
After playing around in pry it became clear that the params were coming in without the required :game
param. They looked like this:
Rails was able to guess that :score
(an attribute of the Game model), belonged to :game
, and so was permitting it into strong params. But user_name
was being left out and so games were unable to save.
The data I was sending in from my front end was as follows:
I collected the data (score and name) into constants from their respective places on the page and passed them to the postFetch
function.
function formHandler(e) {
e.preventDefault()
const formScorePhrase = document.getElementById(“score”).innerText
const formScore = parseInt(formScorePhrase.replace(“Score: “, “”))
const formName = document.getElementById(“name”).value
postFetch(formScore, formName)
}
I created a bodyData
object to hold both pieces of data and passed that in to the body:
attribute of the fetch
function.
function postFetch(score, user_name){
const bodyData = {score, user_name}fetch(endPoint, {
method: “POST”,
headers: {“Content-Type”: “application/json”},
body: JSON.stringify(bodyData),
})
Turns out, all I needed to do to satisfy my params requirement was create a nested bodyData
object that placed {score, user_name}
as the value of a game:
key.
function postFetch(score, user_name){
const bodyData =
{game:
{score, user_name}
}
Now the params coming into my backend upon postFetch
look like this:
My strong_params requirement is satisfied and scores will save!