4 ms·
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
by alangpierce 11y ago
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?
- salex89 11y agoNot that I had a similar situation, but how to implement actions such as shuffle or draw and make them-resource like?
- SilasX 11y agoI 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.
- salex89 11y agoI 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 :) .
- oneeyedpigeon 11y agoYou 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 :)
- hartror 11y agoResource 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.
- vladharbuz 11y agoSome 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.
- hartror 11y agoJust 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-resource-modeling 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.
- geon 11y agoI 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.
- vladharbuz 11y agoIf you do this, you are basically doing RPC, so in reality you've given up any RESTfulness.
- lmm 11y agoYou've gained the useful parts of REST - lower transport overhead than SOAP, and being able to call it from a browser.
- geon 11y agoPlus, for the CRUD parts, it's still REST.
- michaelmior 11y agoWhat's stopping you from calling SOAP APIs from a browser? Most use HTTP as the transport mechanism.
- lmm 11y agoSOAP 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.
- storea 11y agoI for one like get requests. They are much easier to play with.
- jalfresi 11y agoI'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
- jalfresi 11y agoWhy the down vote? This is a perfectly acceptable approach! If you're going to down vote at least respond with an argument why
- hartror 11y agoProbably because not only is the proposed API not REST it isn't even HTTP.
- jalfresi 11y agoBoth 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?
- hartror 11y agoI 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_arch_style.htm#sec_5_3_1 https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arc...
- Fogest 11y agoWhat exactly is hypermedia? Wikipedia is not helping me understand what would not to be changed to make the api use hypermedia?
- MarkyC4 11y agoLook up HATEOAS. I find Spring's documentation to be pretty good at explaining: http://spring.io/understanding/HATEOAS http://spring.io/understanding/HATEOAS