Just because Jeff and I are working primarily on one game does not mean we have not discussed others. One such game is an online collectible card game (CCG). Our efforts on that project amount to little more than four hand-written pages on notes. But today I spent three hours driving to a doctor’s visit, a couple of hours there, and then three hours driving home.
It gave me plenty of time to think, which I spent pondering about an aspect of the plans for the technical implementation.
Do You GET Idempotent?
The HTTP standard defines a number of methods, which are easy to think of as the ‘verbs’ we use when ‘speaking’ HTTP. We use one of those methods whenever we make an HTTP request. The two most common have to be GET and POST. I have no data to support that, but your browser uses GET every time you visit any site and POST the majority of times you submit any form.
The two methods are different in their intended use, but the important difference I thought about today is the fact that GET is idempotent while POST is not. This means that perform the same GET request once should have the same effect as performing it ten times or a hundred times. Or an easier way to think of it: idempotent methods should have no side-effects. When you send a POST request, e.g. submitting a
<form> on a site, you are sending a request that will have side-effects. For example, the POST request for creating an account on Twitter is not idempotent because attempting the same request multiple times has different results; after the first request you will be told the account already exists.
In contrast, every GET request should be idempotent and without side-effects. But web developers know that in ‘the real world’ this is not how things work. Plenty of web sites and web applications stray from the standard by invoking side-effects or causing changes in response to GET requests. Have you ever seen a site with URLs like this?
The idea is that the hypothetical web application in question receives this GET request, looks at the
action parameters, and based on those will remove that user from the system. Repeating the same GET request cannot be idempotent because how can it have the same results? This is the heart of idempotent HTTP methods and an unfortunately common example of how a lot of sites employ a poor practice that runs contrary to the HTTP standard.
Note: If you are a web developer working on a web application that accepts requests like this then I strongly suggest modifying it to use more appropriate HTTP methods instead. For example, instead of using a GET request on a URL like the above to delete a user you should write your application to accept and process the DELETE method, one which is standard in HTTP. Said example request would look like this (n.b. the placement of the user ID in the URL as opposed to being a parameter):
Idempotent Way to GET Cards
I do not want to discuss the finer details of the online card game we are creating because frankly there are no such details. All we have done is brainstorm some broad ideas to keep on hand for a future project; I must stress again that we’ve invested no serious effort whatsoever in this project thus far. So the best way I can describe the game is this: it is an online, simplified version of Magic the Gathering.
The plan is for the game to be browser-based so that it can reach the largest possible audience; any device that can run a web browser should be usable for paying the game. That technical goal naturally facilitates and encourages the use of ‘best practices’ for the API, i.e. the URLs which the browsers will use to interact with the game. That idea led me to think about this question during the dull hours of car travel today:
“How can the game let browsers fetch cards with an idempotent
GET request since the data for some cards will inevitably change?”
For the sake of example we will say the game’s web server accepts requests in the form of
GET /card/$id as the standard way to present data about a card, where
$id is a number unique for that card, e.g.
GET /card/781. Ideally that request will be idempotent and always return the same data. But we have to be realistic and admit that cards will change. As the game progresses the stats of some cards will change, and some cards will be removed from the game entirely. When that happens should the GET request return the modified data? That would be easy to do in terms of sheer programming, but it would not be the most proper way to handle the situation if we revere the words of RFC 2616 as holy—and personally I do.
301 to the Rescue
HTTP defines a variety of response status codes, broken up into related groups. All codes in the range 300–399 deal with redirections, and therein lies the answer to my question above. I decided that when a client requests information about a card that’s changed the server should always send back a 301 Moved Permanently response.
The 301 response code tells clients that the data they’re requesting can be found at a new URL and that they should always use that new URL in the future. Changing a card in our game—let’s say we increase its attack power—would create a new URL for that updated version of the card. The old version would still exist because cool URIs don’t change. So the older version of the card data would remain available, but requesting it would evoke a response like so:
HTTP/1.1 301 Moved Permanently Location: http://example.com/card/782
The game clients, i.e. browsers, could then cache this information so that in the future requests for that card would go straight to the new URL. This approach would allow us to make changes to cards while remaining true to the spirit and letter of the HTTP/1.1 standard. Nowadays popular web application frameworks tend to make it easy to create responses for redirections like this, so I suggest investigating the documentation of any framework you’re using to see what help it can give you in this regard.
The situation I thought about today and discussed above is not unique to gaming in any way. All kinds of web applications face the issue of deciding how to deal with idempotent requests on URLs which represent information that could change in the future. Thoughtful use of HTTP redirection codes is a great way to address this issue, and in the future I will write more about this and other HTTP methods as they pertain to REST, something many web application frameworks do their best to support, and in the past I have mentioned such frameworks for PHP which I hold in high regard.
I recognize this article does not explain the value of idempotent HTTP methods, and again I’ll write about that in the future. But if you already understand that value then I hope this provides some insight about the usefulness of HTTP’s redirection facilities.