Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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


Why the down vote? This is a perfectly acceptable approach! If you're going to down vote at least respond with an argument why


Probably because not only is the proposed API not REST it isn't even HTTP.


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?


I haven't, you are right I probably should.

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.

https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arc...


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.


Some of these don't really make sense.

    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".

http://www.thoughtworks.com/insights/blog/rest-api-design-re...

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 agree with your comments and take them as evidence that REST is exceedingly difficult and complex for these use cases.

You can create new resources, just as I have, it just doesn't make sense to "POST /cards" when you're not making a new card at all.


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.


If you do this, you are basically doing RPC, so in reality you've given up any RESTfulness.


Hence, "a bit like de-normalizing your database".


You've gained the useful parts of REST - lower transport overhead than SOAP, and being able to call it from a browser.


What's stopping you from calling SOAP APIs from a browser? Most use HTTP as the transport mechanism.


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.

[0] https://github.com/oozcitak/xmlbuilder-js/


Plus, for the CRUD parts, it's still REST.


What exactly is hypermedia? Wikipedia is not helping me understand what would not to be changed to make the api use hypermedia?


Look up HATEOAS. I find Spring's documentation to be pretty good at explaining: http://spring.io/understanding/HATEOAS


Not that I had a similar situation, but how to implement actions such as shuffle or draw and make them-resource like?


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 :)


I for one like get requests. They are much easier to play with.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: