Using POST

Ok, our little application is already pretty cool, isn’t it?

However, there’s one big problem that we definitely want to fix: The HTTP specification says that GET requests should be safe, and idempotent.

What does that mean? Wikipedia says:

“Idempotence is the property of certain operations in mathematics and computer science, that can be applied multiple times without changing the result beyond the initial application.”

Obviously, whenever we reload the URL our application adds the name again, and again, and again. Since we do this in response to a GET request we do not comply with the HTTP specification: we do change the result, the HTML that is returned.

When we store, modify, or delete data in our application we also say that we “change the state” of the application: It goes from “3 names stored” to “4 names stored”.

GET requests should not modify the state of our application according to the HTTP specification. They should only “get” what’s already there, and not change it. So what do we do?

The appropriate HTTP verb (request method) to use for this kind of request is POST. The result of a POST request does not need to be idempotent, and it’s basically up to the application to decide what to do with it. In modern applications POST usually means “add this thing to the collection”, where “the collection” is defined by the path: In our case we want to add a name to the collection monstas.

In order to tell the browser to send a POST request instead of a GET request we add this as an attribute to the <form> tag like so:

<form action="/monstas" method="post">
  <input type="text" name="name" value="<%= @name %>">
  <input type="submit">
</form>

When you reload the page, and try to submit the form again, you’ll get Sinatra’s 404 (Not Found) page though: “Sinatra doesn’t know this ditty.”

Of course!

We do not have a route for POST requests, yet. Remember how the HTTP verb is a key part of the HTTP request? And Sinatra wants us to use these verbs in order to define our route.

So let’s add one, and move the logic for storing the name to the new route:

get "/monstas" do
  @name = params["name"]
  @names = read_names
  erb :monstas
end

post "/monstas" do
  @name = params["name"]
  store_name("names.txt", @name)
end

Cool!

Our get route is now idempotent (it does not change any state), and we also have a post route for the same path, and we store the name if there is one.

However, what should we now send back to the browser in response?

For starters, we could just send a little confirmation:

post "/monstas" do
  @name = params["name"]
  store_name("names.txt", @name)
  "Ok!"
end

Hmmmm. Ok, this isn’t too bad, we could make this a proper HTML template.

However, where would the user go next? Shouldn’t we display the updated list of all the names next?