23 ms·
REST Anti-patterns
- EugeneOZ 10y ago"Correct POST /accounts/4402278/close" bullshit. POST should not contain all details in URL. POST can have body and each query should not be unique. Stopped reading after that.
- zeveb 10y agoI disagree. In the example, account 4402278 is a resource represented at /accounts/4402278; actions on that resource should be performed … on that resource, not on some other resource (e.g. all accounts, at /accounts). This also gives increased future-proofing, since someday one might have uncloseable accounts; those accounts could still live under /accounts, but would simply have no /close endpoint (as opposed to having /accounts/close, which sometimes works and sometimes doesn't).
- deleted 10y ago[deleted]
- EugeneOZ 10y ago"close" is a verb, so it's not a resource, it's a method and 4402278 is just argument of that method.
- recursive 10y agoURLs should... well... locate a resource. The resource is an account.
- EugeneOZ 10y agoOnly when it's GET request. This request will not return you any resource information, with id if account or without.
- amk_ 10y agoWhat about DELETE?
- EugeneOZ 10y agoDELETE is for deletion only, this request can't have body, so only what we can do is send id in query. When we want to remove multiple resources we either should send multiple requests (what is slow), or combine multiple ids in query (what is ugly and non-idiomatic).
- recursive 10y agoIn your mind, what does URL stand for? It's an acronym.
- EugeneOZ 10y agoDo you really expect to get resource data when you send POST or DELETE request? If yes, then web (and REST) is not as perfect as you expect.
- blowski 10y agoThe REST design pattern does require that your URLs are structured in this way, and I don't think that's really open to debate. Whether or not all HTTP APIs need need to be RESTful is a different issue.
- darkhorn 10y agoYeah, just PATCH /accounts/4402278 with parameter ACTIVE: 0
- macca321 10y agoHow your URLs read (and if you PUT or POST) is entirely unimportant if you are doing hypermedia/HATEAOS/REST as Fielding envisages it (http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...). "If the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period." If you're doing REST as-in JSON-over-http + a docs page, knock yourself out. edit: Also see: http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
- beat 10y agoThese aren't really antipatterns. They're just bad design. Not all bad design is an antipattern.
- blowski 10y agoMuch sexier and authoritative sounding name, though.
- vinceve 10y agoWhat about error representation? Most of the time I find that actually representing errors is the hard part of creating a REST api. You usually want some representation of each field, plus maybe a generic one. I always have troubles with this. Is there a standard or a proper way to define/do this? I would be very interested.
- 3pt14159 10y agoIf you are returning multiple errors of different types, (403 for the forbidden change and 422 for the incorrectly set field) then you are supposed to role it up into a generic 400. Google JSON API for more information about the right way to represent multiple errors at a time.
- idbehold 10y agoThere's an RFC for that! https://tools.ietf.org/html/rfc7807 https://tools.ietf.org/html/rfc7807
- nmgsd 10y agoNice to PATCH mentioned. Highly useful and often overlooked.
- strictnein 10y agoYes. Just make sure everyone is on the same page for PATCH. Multiple ways to handle it, and two very different RFCs. On an old project our backend team agreed to support PATCH, but they wanted it based on this RFC: https://tools.ietf.org/html/rfc6902 https://tools.ietf.org/html/rfc6902 The front end team wanted something more like this (before it was ensconced in an RFC): https://tools.ietf.org/html/rfc7386 https://tools.ietf.org/html/rfc7386 To me, RFC7386 more accurately represents the idea of just sending the updated content.
- fzilla 10y agoOther commenters are correct that POST /accounts/4402278/close is not right (and also fairly hilariously contradicted in the next section). Account status (open, closed, suspended, whatever else) is a property of the account, in the same way that the account owner's name is a property of the account. If you went to all the trouble to represent each account as its own resource, which I assume responds correctly otherwise to GET, POST and PUT requests, why is this one property special enough to get its own endpoint? Why would you not just PUT or PATCH the account to change the "status" property to "closed"? In my experience, programmers typically break best practices in this way when there is special logic that needs to happen when the property changes. In other words, PUT is fine as long as it's only overlaying new data and not triggering other processes, but closing an account kicks off a whole host of internal processes at the business, so it seemed reasonable to someone to make it a separate endpoint. This either represents a friction between good API design and what programmers find reasonable ("I have to make a bunch of special things happen, so I'll bundle them into their own function and expose them"), or the API framework isn't flexible enough to supply hooks to insert logic at field-level changes, or both.
- strictnein 10y agoThe action is "close". The status may or may not be "closed", but that's not what he's doing. Read a little farther. In the brief example JSON for the accounts, there is no "status" property. In fact, there are four actions available on the account: {"rel": "deposit", href: "/account/4502278/deposit"}, {"rel": "withdraw", href: "/account/4502278/withdraw"}, {"rel": "transfer", href: "/account/4502278/transfer"}, {"rel": "close", href: "/account/4502278/close"}
- mikio 10y agoWhy is POST /accounts/4402278/close correct in the first example? According to the rest of the post, PUT should be a better option. Closing an account seems to me like its updating a resource, not creating one. Also, will calling that url close the account multiple times? POST is not supposed to be idempotent
- binarymax 10y agoOr, if you really want to be dogmatic, use DELETE when destroying something...in this case the customer account.
- arethuza 10y agoI don't think I would assume that "close" is the same as a delete.
- 72deluxe 10y agoThen why not cater for your own verb instead?
- caseysoftware 10y agoBecause you should never, never, never create your own verbs. The main reason APIs have become ubiquitous is that we have a shared understanding that follows us from API to API. That includes the concept of verbs, response codes, and how to interact with the nouns/resources. If you add verbs, you're breaking that agreement. Also, from a technical perspective, some of the internet infrastructure - servers, routers, etc - drop things they don't recognize. Heroku for years was dropping the PATCH verb despite recommending its use for customers' APIs.. and PATCH is in one of the specifications! Imagine what would happen to your custom verb. Don't create your own. Stick to the standards.
- zeveb 10y agoClosing it may not delete it, but simply mark it as closed.
- marcelocure 10y agoright, POST is not supposed to be idempotent. However, if you do a PUT on it, you are updating a resource, closing an account may involve more processes than only updating the given resource. Ok, but why did I say it should be a POST even if POST is not supposed to be idempotent? In my point of view, POST should only be idempotent when creating resources, in this case we're acting in a resource, the /close is a process that happens on that resource (4402278), not a create/update/delete/retrieve. The best way to do it would be a POST in my opinion. Also, if we want to update only a piece of the resource, it should be a PATCH. PUT is supposed to update the whole resource.
- tremon 10y agoStrictly speaking, if you do a PUT on it, you are not updating a resource, you are replacing a resource. I know you basically say the same thing, but "update" is too ambiguous in this case. That is also why PUT is idempotent: it replaces the complete state of the referred resource. Similarly, POST is not idempotent because it modifies (part of) the resource's state while leaving it otherwise untouched.
- PaulHoule 10y agoThis misses the elephant in the room. The REST-inspired API that requires 10,000 API calls to do something that could be done in 1. This is a disaster from a simple performance perspective. There is no simple way to build transactions on top of REST so if you need to do something that involves updating more than one data record you really are best off updating them all in one API call. When you are writing a web front end this is all the more acute because choreographing a complex interaction with a server is a PITA with asynchronous communications. If Fielding's thesis went in the trash and got replaced with "one click, one API call, update the UI" we'd hear a lot less carping about how awful the web platform is. Careful reading of the http spec is a road to hell anyway because 80% of it is dark corners that aren't really used or implemented.
- macca321 10y agoThere's nothing to stop someone putting resources on an api which do the one thing a client needs to do. And what you describe nothing to do with REST, it's to do with overly fine-grained API design, without taking your users into account.
- PaulHoule 10y agoI have frequently seen it done in the name of REST, and had people argue that an effective API is an evil RPC.
- macca321 10y agoFirst and foremost an API which services it's clients need well is a good API. There are many people arguing things that aren't REST (e.g. pretty urls, media type based versioning) in the name of REST, and there are also (fewer) people trying to talk about things that are REST (e.g. HATEOAS) in the name of REST. This makes it (understandably) easy to blame REST due to the large amount of confusion. In fact, I did a Bad Thing in my other comment by pointing out that REST-as-widely-understood isn't REST as all the mature individuals who are interested in REST APIs have taken to referring to them as Hypermedia APIs in order to avoid having to rehash the differences and kick off a flame war. I wouldn't have done it if OP hadn't mentioned HATEOAS.
- barrkel 10y agoHATEOAS is much less important for programmatic APIs. You either hard-code knowledge of the URL scheme in the API client, or you hard-code knowledge of the payload schema in the API client. There isn't a huge amount of difference here IMO, especially if you have some kind of versioning mechanism in your URL routing. Hard-coding URL scheme permits more pipelining and concurrency in the client. Embedding URLs in payloads forces sequencing. This alone can make the templated URL scheme a win for interactivity.
- macca321 10y agoThe dream is that your client can dynamically update itself or seek out ways to process new, incomprehensible things* that it encounters while traversing a rest request (see "Code-on-demand in REST literature). If you think this is possible in the wild or not is another matter. *each nugget of data can be versioned and meaninged independently
- barrkel 10y agoThat, and the semantic web, and pluggable components from different vendors using standardized interfaces, and other architecture astronaut ideas of software composition. Any day now.
- sidlls 10y agoSounds a bit like UML+Rational, visual programming, and every other one size fits all panacea, to be honest. The whole notion of HTTP as an application data transport is terrible. We've hacked at it to create a great number of wonderful web based apps, but I can't help thinking there's a better way.
- einrealist 10y agoIf your implementation can rely on Hypertext, you can build it different. You can build a state machine where parts of your application are "componentised" without knowing the context of a resource.
- jackcosgrove 10y agoNo mention of the biggest single problem with REST as it's practiced: POST /login. You should use POST /session instead.
- rahkiin 10y agoWhat if i use JWT and thus have no concept of 'session' resources? You would actually create a token using username and password.
- jackcosgrove 10y agoMy critique with POST /login is that it's a verb, not a noun. Whether the session is stored on the server or the client is the same regardless of the verbiage. In both cases a client-side session is a bit of semantic abuse but HTTP does not have verbs for creating resources on the client. I think a JWT token represents a session, since it can expire and be disposed when logging out.
- bschwindHN 10y agoIt's all made up words anyway, who really cares?
- deleted 10y ago[deleted]
- KillerRAK 10y agoResource verbs in POST URLs are almost always a bad idea. You POST a command via the body to the resource and should expect a result in turn. Though I do understand the idea that URLs with verbs allow the API to be self describing via links, I think it's a bit naive (and verbose) to think a user will get all the info they need from hitting the API. RAML and its variants do a great job of conveying this information.
- carsongross 10y agoThe initial and fundamental problem is a misapplication of REST. To take advantage of REST, you need some form of hypertext, not raw JSON APIs. Additionally, on a practical level, you need the end points to be relatively coarse grained, which can be achieved if you design the API for your UI use case, rather than general data access: http://intercoolerjs.org/2016/01/18/rescuing-rest.html http://intercoolerjs.org/2016/01/18/rescuing-rest.html Shoehorning HATEOS in JSON APIs is a category error: http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.html http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.ht... Long story short, REST: you are all doing it completely wrong.
- Falkon1313 10y agoThe issue with HATEOAS is a little overstated. You don't need a full AI with independent agency. What you need is a clear media type that corresponds to some domain concepts that both the client and server agree on. The client and server don't even have to totally agree, as long as there is some overlap. The server may provide functions that the client doesn't care about, and the client may care about functions that the server doesn't provide (but are provided by another server). Failure cases are: - media types totally disjoint (this wouldn't work for a human either, since the server couldn't do anything that the user wants to do with the resource) - media type misunderstandings - there is overlap in the functions, but what the client intends and what the server does are two different things, because their concept of that domain function is different (that would also be the same problem for a human) - media types undefined - everyone is just passing random unlabeled JSON globs, so any domain concepts have to be hardcoded equivalent on both sides (this is one case where a human might be able to intuitively guess the right thing, but only if their mental model happens to match the server's model) The solution is to document the domain concepts of the media types that fit your domain from both perspectives, find the places that don't overlap or match in name but not semantics, and decide what to do about them.
- carsongross 10y agoWithout agency on the client side, supplied by humans, how is it advantageous to send an unknown number of potential actions from the server?
- niftich 10y agoThe content of the blog post is okay, but the premise is flawed. Most of us have reluctantly accepted the abuse of terminology that happens when everyone calls these APIs 'RESTful' -- but they're not. They're inspired by REST, but cargo-cult took the easiest-to-implement pieces all the while pretending to stand on some moral high-ground about not being openly RPC because 'RESTful is the right way'. This results in a worst-of-both-worlds situation, where most of these API don't have custom mediatypes that I can Accept: header for, don't have custom link relations one can programmatically traverse, and some poor person had to contort their data model to come up with plausibly 'resourcey' objects to make HTTP calls against, all the while losing simple reassurance you would've gotten from an uncool RPC endpoint. HATEOAS is critical to REST -- it being nothing more than a terribly obtuse rendition of the ideas behind how the web (but more importantly, the semantic web) works. This isn't the usual rant complaining about how HATEOAS is most implementers' afterthought; this is the rant about how HATEOAS is the entire damn point; without it you're just squirting JSON on the wire and using HTTP as a transport because it doesn't get blocked on a middlebox. And now that we've retrofitted schemas into JSON, and moved half our APIs to use CORS/CSP-needing PUT/DELETE methods, and require OAuth for more than half of the requests, the original advantages of this scheme are entirely gone -- you can no longer just muck around in some half-baked javascript, parse out a single field, discard the rest and surface it in a 'web 2.0 mashup'. And when the vendor supplies the SDK anyway (even in Javascript), the exact form the messages take on the wire is entirely irrelevant. I hope the new wave of RPC (helped by the bi-directional multiplexing transport protocol inexplicably known as HTTP/2) becomes the new fad and kills this awful fumbling with cargo-cult fake-REST once and for all.
- rv11 10y agoHATEOAS is critical to REST -- it being nothing more than a terribly obtuse rendition of the ideas behind how the web REST APIs must be hypertext-driven. http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...
- SEJeff 10y agoAlso an absolutely fantastic article from Jacob Kablan-Moss, one of the three original founder of Django on REST anti-patterns (not Django specific at all): https://jacobian.org/writing/rest-worst-practices/ https://jacobian.org/writing/rest-worst-practices/ Every web developer should read that ^
- currywurst 10y agoAre there good guidelines on complex patterns in modeling your resources ? E.g long-running operations involving multiple entities.
- currywurst 10y agoAre there good guidelines on complex patterns in modeling your resources ? E.g long-running operations involving multiple entities.
- 0xmohit 10y ago- REST Anti-Patterns [0] - API Anti-Patterns: How to Avoid Common REST Mistakes [1] [0] https://www.infoq.com/articles/rest-anti-patterns https://www.infoq.com/articles/rest-anti-patterns [1] http://www.programmableweb.com/news/api-anti-patterns-how-to-avoid-common-rest-mistakes/2010/08/13 http://www.programmableweb.com/news/api-anti-patterns-how-to...
- laurent123456 10y ago> POST /accounts/4402278/close I'm not really sold on this one since "close" is not a resource. I think I would do: PATCH /accounts/4402278 with data `{ "status": "closed" }`
- orware 10y agoI'm currently working on a Database Proxy API Project I cooked up (essentially creating a way I so we could query our different MySQL and Oracle databases using a RESTful API) and it makes sense for me (from the RESTful perspective) to use a GET request to send the query. However, I know that the request body (a JSON string or a JSON array that could contain one or more queries, along with one or more different decryption keys allowing the server-side to retrieve the connection details that are encrypted on the server) could be quite long (we have some massive SQL queries that get used elsewhere and could potentially be used with this new API once it's available) and I also didn't like the idea of some of the more sensitive information being in the URL, so I decided to implement it as a POST request instead of a GET. It feels wrong (again from the RESTful perspective since I'd like my API to be fully RESTful) but from a user point of view I know I'm making the right decision. If anybody has better ideas though, please let me know!
- partycoder 10y agoCache control and misuse of e-tags is also a common problem.
- jack9 10y ago> looking at the URI the consumer must understand all about the given resource Just because? No. > The HTTP methods must be used to give the intent of the action that is happening. That interpretation (PATCH, DELETE, etc) is not "correct", which is endlessly maddening. It was a possible scheme, just like a waterfall process was a possible development scheme. There is no correctness implied. See: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch..., http://cafe.elharo.com/web/why-rest-failed/ http://cafe.elharo.com/web/why-rest-failed/, http://www.artima.com/lejava/articles/why_put_and_delete.html http://www.artima.com/lejava/articles/why_put_and_delete.htm..., http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post I really get sick of these purity guidelines that have no practical use other than to say "see, it works" which isn't compelling.
- whamlastxmas 10y agoDisappointing to see so much downvoting in a thread when people are just giving their opinions. Seems like a lot of downvote misuse on HN recently.
- velox_io 10y agoQuestion 1#: Is there a reason PATCH is used (or even needed?), instead of PUT to update records? (Create, Read, Update & Delete => POST, GET, PUT & DELETE) seem like a natural fit, why complicate things? Question 2#: What is the consensus around custom verbs? While I've never gone down this route, a few times I have been tempted.
- Falkon1313 10y agoRe #1, PATCH is useful because PUT requires that you replace the entire resource even if you just want to change one little thing and to do that safely, you need the now-current-latest representation of the full resource. A common case is looking at resource A, which is related to resource B (and therefore you have B's ID, but not its full data), you can patch resource B specifying just its ID and the change that you want to make without having to fetch the whole thing first. Also, PUT can conflict (or overwrite changes) if something else has altered the resource since you fetched it. PATCH is more granular, so you can make it a lot less likely to conflict or overwrite (in addition to being more efficient). Re #2, I don't know about consensus, but usually when I think that I could use a custom verb, some more thought reveals that I could instead use a standard verb on another resource that I haven't spec'd out yet (like the comments about having a 'transaction' resource representing details of a transaction instead of a 'transfer' verb).
- sopooneo 10y agoHypothetically, with PUT, whatever you send to the server should now comprise the entirety of that resource. So if all you PUT is a last name to /people/4335, then all of a sudden person 4335 consists of nothing more than a last name alone in the void, and not even blank fields beside. With PATCH, on the other hand, you can confidently send the value of just one updated field, without your users worrying about their metaphysical well-being.
- tvjames 10y agoUseful article for someone needing help in revising a URI naming scheme, but beyond that could end up being harmful to that person's greater understanding of building "RESTful" systems. For example, in the Idempotency section, the author states that for GET requests "no change in application state should occur" which is good, but then also states "the response should always be the same" which is incorrect. There's nothing that says the response cant change, if the resource has changed, but the constraint is that it cant cant _as a result_ of the GET request. This is an important distinction. DELETEing a resource twice, the second delete should be a NOOP, not a 404. There's some excellent NDC Oslo & London talks covering more in-depth RESTful topics that I'd recommend checking out if the content of the article is an eye opener for you. https://vimeo.com/131631886 https://vimeo.com/131631886 https://vimeo.com/131196782 https://vimeo.com/131196782