Hey, you did a thing and told people [0]! Good for you. Is it perfect? No idea, but from reading the comments here a lot of people don't think so. Who cares though [1]! You're so far ahead of so many people because you actually did something.
Next time maybe all your GETs will be RESTful, maybe not. Either way, you've done a thing, put it out there, and are presumably learning a lot by doing so.
I'm seeing a lot more discussion and debate, rather than criticism.
So good job for that too... I think this Deck of Cards API would make a great case study for analyzing and discussing the varying takes on how a RESTful interface should handle certain situations which aren't as straightforward as the typical blog or todo list examples.
A lot of the time it doesn't feel like that to me.
People release things they have worked long and hard on, only to get a bunch of negative comments picking on holes and mistakes.
The weird thing is they are the lucky ones; it's pretty damn hard to make it onto the front-page of HN now, regardless of the quality of the content. (Don't believe me? Take a look at how many times some articles have been reposted and the disparity in number of votes.)
Getting feedback should be great, but not many of us are good at it. Generally, we only comment on things we care about or find interesting (ignoring the stuff that is so bad it deserves harsh criticism like storing passwords in plain-text), but we forget to say that in the comment and only mention the minor flaw we saw.
I don't have a solution to this, other than to tell posters to be prepared for criticism and to take each up-vote as a major token of respect in what you've built.
* You are mutating state with HTTP GET, this is an anti-pattern for a number of reasons. The common one I use with people I coach is that browsers/proxies will happily cache GET requests unless told not to, but there are a number of other reasons if you read up on REST [1].
* Being a public API in a well known domain this is a good opportunity to make the API self documenting & navigable with a hypermedia format. (eg HAL, Siren, JSON-LD) [2]
I agree. However, this is the first API I've seen where a mutable GET actually makes sense. Drawing a card is definitely a GET. How would you do it instead?
I see where you're coming from ("I'm drawing / taking / GETting some cards"), but drawing cards is not an idempotent action. I would implement it as a POST.
I think it's more of a terminology thing, rather than 'GET me a card' you could 'POST remove 2 cards from the deck' and the side effect is the response tells you which cards were deleted.
But this kind of bike shedding drives me mad because oh my lord who gives a crap just build software and document it properly.
Same here. Working on a large codebase where people "just build software and document it properly" without following standards is not fun. Not to mention that frequently people end up shortening that to "just build software". It's better to get in the habit of following good standards even on small projects before bad habits form.
As others have pointed out the HTTP verbs have meaning at a protocol level, trying to match a verb based on its English meaning is fraught with problems. Not least of which is the proxy problem I pointed out in my original comment.
It is acceptable for a client to retry a request that failed if it is a GET. This could happen if the server got your request but the response got lost, in which case you can't count on the deck having the right number of cards.
Why conform to arbitrary conventions when they don't make sense? Personally I think the best reason to conform to REST is because the user expects a "good" api to conform. Other then that is there any hard reason as to why it's "better" to strictly follow REST or even follow it at all?
Is an api that is consistent, fast and documented well, but uses only GET requests for everything including state changes really a horrible api?
> Is an api that [...] uses only GET requests for [...] state changes really a horrible api?
Yes, it is. Because HTTP clients (including browser) rely on the common method properties as defined by the HTTP standard (GET having no side effects, PUT being idempotent, etc.). The simplest example is caching / proxying, but there is other behaviour.
I strong recommend acutally taking a look at the HTTP standard. The RFCs such as RFC 7231 are well organized and human readable. For the definition of the HTTP verbs, see:
RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
4.2 Common Method Properties
I agree with the spirit of your post, but using the right HTTP verbs can have real, actual consequences because of how some caches and clients behave. You're more likely to accidentally send multiple requests or get stale data when you're using GET.
That's not what idempotent means. It doesn't mean it produces the same side effects every time, it means multiple calls are equivalent to a single call.
I'm not questioning that; I was questioning the statement '"action" parameter is pretty non-RESTful'. If URLs are opaque, why is it non-RESTful? Or is the point that if you're using query-string parameters, then by definition you're using a method that's supposed to be safe or idempotent?
If you used different HTTP methods, you'd already know which one to use, and don't need the action parameter. So the usage of the "action" parameter usually implies that that requests are all the same method. And if we don't want to violate the HTTP standard, that method must be neither GET/HEAD, nor PUT nor DELETE (as surely some actions are non-idempotent).
In other words, an action parameter which is actually used and which doesn't violate the HTTP standard, well, that almost certainly means that all requests are POST requests.
However, if all requests are POST, no matter if they are side-effect-free or not, no matter if they are idempotent or not, then this is not very REST-like.
Note that it is not important whether you think this argument does or doesn't holds for your particular API. My point is that this whole judgement is solely about HTTP methods, and has nothing to do with URLs being opaque.
> If you used different HTTP methods, you'd already know which one to use, and don't need the action parameter.
But the examples given by anilgulechas were 'action=draw' and 'action=shuffle'. Neither are idempotent, let alone safe, so presumably the only method to use in either case would be POST (notwithstanding anilgulecha's suggestion of a PUT).
So we need to distinguish between POSTs requesting a draw, and POSTs requesting a shuffle. Two options:
a) indicate it in the POST's body
b) indicate it in the URL.
If we go for (b), the query string seems as good a place as any.
What have I got wrong? Where would this violate REST (or the HTTP spec)?
Could each draw return a draw_hash, that is usable only once? Not only could you then have idempotent draws, you could easily embed it as hypermedia,a previous hash could be sent to a hand_history to return an array of point in time data
I don't see much benefit in martin fowler's example
<openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
<link rel = "/linkrels/slot/book" uri = "/slots/1234"/>
</slot>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
<link rel = "/linkrels/slot/book" uri = "/slots/5678"/>
</slot>
</openSlotList>
Each slot now has a link element which contains a URI to tell us how to book an appointment.
Well, not really - we can only guess that by the fact that the link ends in "book".
We also don't know if we are supposed to GET, PUT, DELETE, POST.. or what data we are supposed to send to the url to actually make the booking.
Clients of a hypermedia REST API are supposed to have a priori knowledge of the link relations (the "rel" attribute). The fact the rel in this example looks like a URI is sometimes used as a convention (i.e., if the API supports it, a client may do a GET against the rel value, and get back a human-readable description of the purpose of the rel).
In the human readable description of the "/linksrels/slot/book" link relation, information should be given about what generally can be done with such links (like if GET or POST is supported, etc). This allows you to build your client applications. The value of the actual link (the 'uri' attribute) should be completely opaque to the client.
I agree though I expect Fowler may argue that that is what the HTTP verb OPTIONS is for. Amundsen on the other hand likes his hypermedia more explicit and I tend to agree with him.
While this looks to me like a pretty clean and straightforward API, it's definitely not a REST API (not that it claims to be):
* The API doesn't really follow HTTP. It allows GET for state-modifying actions, which breaks assumptions that could be made by browsers, client libraries, caches, and proxies. Also, the "success" field seems fishy; a 200 response code is the usual HTTP way to indicate success.
* The API is not resource-oriented; the URLs "shuffle" and "draw" are verbs that describe the action to take. In a "real" REST API, URLs define conceptual resources (nouns), and you interact with the API by interacting with those resources.
* The API does not use hypermedia.
Hypermedia and HTTP compliance are discussed elsewhere in this thread, but I wonder if others have an opinion on the fact that the API isn't resource-oriented.
This "resource-oriented" rule has always bugged me about REST APIs. My impression has been that REST advocates claim that pretty much every API can and should be implemented with a CRUD-like interface (where the entire API consists of performing GET, POST, PUT, and DELETE on a conceptual set of resources), but it seems like that would be really awkward for this "deck of cards" use case. Is there value in trying to use resources here? What might it look like?
I'm not sure that a CRUD like interface would be best for something like this. In addition, as this is very much an API and the returned JSON changes depending on the action called on the resource, this looks to me like the ideal candidate to create a custom media type for. With that option open, we can simply define our own request verbs like so:
SHUFFLE /api/deck/<deck-id>
DRAW /api/deck/<deck-id>?count=2
Things become a lot simpler to use, document and implement.
I'd actually take advantage of the custom media-type option and return something much more succinct:
DRAW /api/deck/<deck-id>?count=2
200 OK
8♣
Have the response body be UTF-8 and use the card face runes in the response. Nice and simple.
You dont necessarily have to map your REST API to existing HTTP verbs and media types - in fact the real benefit in a use case like this is that you can implement something appropriate
Both REST AND HTTP allow you to specify your own verbs and media types! What I was suggesting was that the semantics of the API could be better expressed as its own media-type with specific semantics rather than try to force the existing HTTP 1.1 semantics on it.
Think WebDAV; specify the API via an RFC like document and implement it that way the end result is not only easier to use, document and implement, other providers could implement compatible API implementations.
Seriously, has anyone here actually READ Fieldings paper?
But it wasn't hard to find something about standard methods and media types.
> REST enables intermediate processing by constraining messages to be self-descriptive: interaction is stateless between requests, standard methods and media types are used to indicate semantics and exchange information, and responses explicitly indicate cacheability.
Thank you for your honesty. It's refreshing and I appreciate it.
I'm not sure if your response is an attempt at a counter argument, but the line "... standard methods and media types are used to indicate semantics and exchange information, and responses explicitly indicate cacheability" is exactly the point I'm making.
If the original author was keen to make their API RESTful (which I'm not particularly sure they are. Aside from the couple of minor issues people have already raised, there is nothing wrong with a JSON API served over HTTP. It's just not RESTful), they would standardize their media-types (by registering them with IANA) and document their custom HTTP verbs via a RFC. That is what is required to be truely RESTful.
In the example I gave, showed that by adhering to the principles of REST, they could actually simplify their API dramatically whilst allowing others to lean on their work (by implementing their own services that use their newly registered media-types).
Again, all this is documented in Fieldings dissertation. It is dense reading and very academic, but it's worth reading. I personally found it very enlightening.
The original API wasn't REST and wasn't claiming to be either! The commenter is right, this is a clear case for an RPC style protocol. One using HTTP (and robustly!) makes client creation easier, but it's still RPC and doesn't need to pretend to be REST.
Resource oriented and its implication of nouns as opposed to verbs doesn't have to equal CRUD. I can see a case for bending the nouns rule of thumb in this domain, for example I might have an api like so:
POST /decks : returns a url to a new deck
GET /decks/<id> : returns a deck
POST /decks/<id>/draw : returns a url to a card
POST /decks/<id>/shuffle : returns a url to the deck
GET /cards/<id> : returns a card drawn from a deck
The draw/shuffle verbs arguably could be implemented like this:
POST /cards : returns a url to a card (post body having deck id)
POST /shuffles : returns a url to the deck (post body having deck id)
The cards POST makes a lot of sense, now that I look at it I think I would use that interface. And you could argue that the shuffles resource makes sense as at some point you may want to record and share when someone shuffles the deck.
POST /decks/<id>/draw : returns a url to a card
POST /decks/<id>/shuffle : returns a url to the deck
Are you adding a "draw" to deck <id>?
POST /cards : returns a url to a card (post body having deck id)
POST /shuffles : returns a url to the deck (post body having deck id)
Are you creating a card or a shuffle?
Here's how things should be, in my opinion:
DELETE /deckCard/<deckId>
This removes a card from an abstract entity representing the relation between decks and cards. Thus, it draws a card and returns it, sort of like popping something off a stack.
PUT /decks/<id> with body {shuffle: true}
This edits the abstract "shuffle" attribute of deck <id>, shuffling the deck. This is indeed odd, but I'm afraid it's the best you can do for a mutating request. I would recommend a non-mutating request that makes a new deck out of an old deck, perhaps using "source" as an abstract attribute.
POST /decks with body {source: <oldId>}
It just goes to show that while REST is a good standard for CRUD operations, and is surprisingly extensible for non-CRUD operations, it can get confusing and become a real pain. Should one just deal with it or move to e.g. RPC? I haven't figured this out.
Just because a thing, such as a draw or a shuffle isn't a physical thing doesn't mean it cannot exist as resource. Programming is all about creating abstractions, so why cannot I create a shuffle or a draw resource? I could even record them and share them on their own endpoints for clients to view.
Commenting directly on you suggestions:
DELETE /deckCard/<deckId>
This means each DELETE on this url would result in a different deck state which isn't idempotent as DELETE is mean to be.
PUT /decks/<id> with body {shuffle: true}
Same with this, the resource would end up in a new state each time making it unsafe to do repeatedly as the HTTP spec says.
I think the issue is REST is taught with a CRUD view point and people have difficulty thinking about it in other ways. Also the English meanings of the HTTP verbs get confused with their HTTP meanings which doesn't help.
I liked this blog post which talks about the concept of "REST without PUT".
edit: you've edited you comment since I started my reply:
I like your idea of replacing shuffle with a new deck:
POST /decks with body {source: <oldId>}
You could then do things like lock the source deck which would make it easier to implement a multi-client system where you cannot draw from a deck until you have been informed it has been shuffled.
I would approach it from a little further away conceptually.
A deck only makes sense in the context of a game, a draw only makes sense in the context of moving the card from the deck to some other container (hand, discard pile, arbitrary pile on the table).
I also like to model games as sequential actions (because that's how they work) so my API would be more like:
GET /game/<id>/turn/<index>/decks/<id>/card/<index> <- 1 or 0 for top card, other numbers to view more than one
Then your draw action is a move the card from the deck to one of the other places, either with a POST to the new place, that redirects you to the next turn of that place, or with a PUT to the card itself if you have some "location" field on the card that can be updated.
The question that strikes me though, is whether it makes sense at all for a client to be controlling the draw in a game of CaH, as it's not an optional step.
Why not just have the model automatically put cards back in the players hands when they make their moves?
I like my REST interfaces, but they only really fit CRUD-like situations. Sometimes, you will want to be less restful to optimize for some real world issue, most likely latency.
I suppose it is a bit like de-normalizing your database for performance.
For a situation like this, I would keep it as restful as practically possible. Since the shuffle mutates the state, it should be a post/put. Since it doesn't create a new resource,you could argue it should be put, but you don't have new data anyway, so the point is kind of moot.
I'd probably collect my custom verbs behind a common path to make it more clear, like
/api/decks/mydeckid/verbs/shuffle
Or to make it stand out more:
/api/decks/mydeckid?verb=shuffle
The response would use http error/success codes like any rest interface.
SOAP APIs tend to use POST even for readonly methods, and tend to use aggressively validated XML that's much harder to write by hand than typical "REST" JSON.
I'm sure why using POST for readonly methods is a problem in the browser. It potentially messes with caching, but it should still function correctly. (Granted, this is probably a poor design design decision, but has nothing to do with correct functionality.)
As far as constructing the XML, you should be able to use something like xmlbuilderjs[0]. That said, I completely agree that dealing with REST APIs in the browser is far more practical.
I guess shuffles and draws should be their own resources, and you POST to it to do that action? You'd then keep a table of shuffle/draw events, which mutate the deck as a side effect.
I get it. I had an idea to make a "shuffled-copy" or a new deck with one less card, made from the original deck. Than you can theoretical rewind your shuffle process, since it's a chain of immutable decks. But I'm afraid that will really consume too much storage for a rather unimportant thing, at least in this case :) .
You could shuffle in a deterministic way, using a smallish seed, and store a list of those seeds which would cut down on storage. I realise that you probably wouldn't want to do this if you're trying to recreate Vegas, but it's an interesting challenge to mull over nonetheless :)
It was a bit fiddly but definitely wins on file size. I would have used SVG for the face cards but they really killed performance with animation unfortunately, so I went with a compressed PNG (20kb for all 3).
Thought about that as soon as I saw it returning rasters. SVG would be best offered alongside as an option; it requires more effort to use than just a png. Perhaps a 'vector' to accompany each 'image'?
Following that link, the first section on the page:
These poker sized cards have been created and hand optimized in various vector formats (.eps and .svg) using the open source .svg graphics editor Inkscape http://www.inkscape.org.
....
Generally, this means the cards are free to use (commercially and non commercially) but must be properly attributed (see "README.txt" file) and (The library itself, not your project) re-distributed only under the same license.
I like the website design but shuffling cards is extremely trivial to code. Also cards are mostly used for gambling games and it's a huge security hole to use a third party api for that unless it's from a very trustworthy source.
One maybe useful thing about this api is the card images, but there are also probably plenty of decks in the public domain.
> Also cards are mostly used for gambling games and it's a huge security hole to use a third party api
Depends. If you're writing a game you wanted the player to trust you might want to use an external shuffling service so you can easily show proof-of-deck after the game is over.
Of course, you could do this simply by giving the player a cryptographic hash of the shuffled deck beforehand, e.g. SHA-256(5H,4D,AS...)
I've worked as a developer for online gambling games for 6 years. We never shuffled the deck of cards. Instead, as player requests another card(s), they are generated using hardware RNG and returned to player. Deck only knows which cards are left in it, but their order is undefined until that call to RNG.
You are correct. As the matter of fact, a bunch of games in majority of cases needed only the next card from the deck, that's why in our heads we almost always thought of "not shuffling at all".
How is that better? You just don't want to spend the time to shuffle a deck if only a few cards will be used? Or you are worried someone might cheat by being able to read card order ahead of time?
If it supported the idea of multiple players, keeping track of what cards each player has, and facilitating passing cards between players; it could be a cool backend for lots of different card games.
I wonder if you could do most of this cryptographically. If you did, you could make a client that could do all of this peer-to-peer. I think you would just need the following to make a wide range of games (many popular ones, perhaps most?):
1. Client can produce a signature that it drew the k'th card from the deck for any k.
2. Client can produce a signature that the k'th card is X if it drew the k'th card.
3. Client can produce a signature that card X came from any arbitrary set of card indices containing k if it drew the k'th card. (This is the hardest one.) If it's easier, you could get most of this even restricting it to sets of cards that the client has drawn.
With just that, I bet you could make a protocol for most card games where no one can cheat, where the central server's role is only to give out information about k'th card one and only one time. Certainly it's enough for blackjack, most forms of poker, hearts, and go fish. Anyone have a game that would be difficult? Maybe one where hidden cards get passed from player to player.
There are so many things a good API would have to do...
Different types of decks, handling of a discard, shuffling a card into the deck, shuffling the discard pile into the deck, handling hands, milling a card from the deck, multiple decks acting as one...
Though the simplicity of this api currently just having a deck that you take cards from is neat.
Neat! Nice work! Some are definitely criticizing, but hey - you'll always have critics. Unless they're following through with a pull-request, don't mind them.
Great job on putting the project out there!
An idea: Multiple options for deck images - obviously would take time on the artwork, but you could set this in your initial call for a deck (Or maybe even make it so you can change it whenever - if someone would have a need for that for some reason). May be fun.
The absence of a pull request does not negate the validity of a criticism. Hacker News is one of the most tech-savvy, intelligent and experienced audience you can find on the web. Everyone sucks, myself included. With that kind of attitude, I would never have learned anything.
Shielding yourself from criticism using an arbitrary criteria only perpetuates ignorance.
I think it was just a fun side project. But as to how it could be used.. just a few weeks ago we were at a hackathon and built a quick poker texas holdem game. A library like that (arguably a bit more comprehensive) would have saved us a few hours.
Was it just a texas hold em ? Or a variation of it ? I assume being a hackathon it wasn't a vanilla texas hold em.
Could you provide some more details and maybe the code ? After seeing this I'm interested to know what else you've done.
So we wanted to learn/play with react native. The project idea was to play a hand of poker online randomly against someone in less than 30 seconds. The fun part is you can only fold or go all-in : )
But how are you shuffling the cards, and how are you identifying unique shuffled decks?
It looks like the deck_id is 12 characters, numbers or lower-case letters. That gives a range of 36^12 or around 4.7 * 10^18. The total range of a physical deck of shuffled cards is 52! or around 8 * 10^67.
It's possible I have misunderstood what deck_id is for.
Wow. I never thought this could be an API. Really good work. I remember I spent a lot of time developing a class that deals with a deck of cards, shuffle and associate the card with a png.
Thanks for asking.
I wanted to build a very simple API, and document it that my year ago self could understand it and actually figure out how to use the whole thing. I don't know if this is a good or bad thing, but I spent more time documenting/designing the single doc page then I did writing the actual API. I also thought the idea was unique enough that it was worth sharing.
The reason for GET and POST is that it was easier for my year ago self to copy and paste a GET url into chrome and hit Enter and see what happens.
Next time maybe all your GETs will be RESTful, maybe not. Either way, you've done a thing, put it out there, and are presumably learning a lot by doing so.
Great work.
[0] http://carl.flax.ie/dothingstellpeople.html [1] http://www.garann.com/dev/2013/how-to-blog-about-code-and-gi...