REST Anti-patterns
marcelo-cure.blogspot.com
marcelo-cure.blogspot.com
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.
I'd love some feedback on this approach. How do other people allow efficient mass changes?
Did you just make up this phrase? It's really good!
Imagine you want to implement a vacation-booking service
as part of an enterprise system. The first step is to
remove any reference to computers or modern technology.
This will allow you to concentrate on the business
objectives of the service, without getting sidetracked by
technological considerations. In other words, it enables
you to separate the “what” from the “how.”
That's pretty great advice.and that was the thinking behind how that system was built. Also this is the paradigm behind
http://www.enterpriseintegrationpatterns.com/
and it must predate the computer.
Here's what you need to do to copy a file to different folder in C#:
var newFile = drive.Files.Copy(null, fileId).Execute();
var newFileInNewLocationreq = drive.Files.Update(null, newFile.Id);
newFileInNewLocationreq.AddParents = newFolderId;
newFileInNewLocationreq.RemoveParents = oldFolderId;
var newFileInNewLocation = newFileInNewLocationreq.Execute();
To be fair it's just 2 API calls but rather verbose. And that's assuming all the calls workThat's not a REST-inspired API. (It might be an API inspired by the kind of naïve mapping of HTTP verbs onto the base tables of a normalized RDBMS schema suggested to people who don't understand DB design, REST, or application design by certain popular web frameworks default "REST" approach, but that's not REST's fault.)
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.
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.
> 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.
Yes, you can do that. I mean, if your really need to do that. Otherwise, your client should check the server resources for whether they represent to correct data. And you can always implement something like implicit transactions, where you create a transaction resource and append data to it or create new resources in a certain context.
EDIT: BTW. You don't have to do everything in REST. You can always "upgrade your protocol". So having a link myrpc://myserver:port is very valid.
> 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.
Then we would end up building clients that always implement 100% of your RPC API. And we would not be able to move resources around. Implementing clients and servers would become an all-or-nothing thing, which really bad.
> 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.
By that you mean, that YOU have only used 20% of the spec so far? So why is it bad in general? I did not see these dark corners.
That should be a non-issue. Consider search queries. They are a simple GET request that returns a collection of many results in one response. You can do the same, you just need to define collection resources or collection semantics for existing resources.
The only challenge is handling partial failures, like when you POST 1000 things, and 22 of them fail validation. So each thing needs a status code in the response and some unique ID so that you can find its status code in the response. (That is separate from the overall request status code.) I have the client send a UUID and the server responds with a collection keyed by that UUID so that it's easy to find, for each thing that you sent, what happened.
Where the server goes from requiring multiple requests to a single request without changing the clients whatsoever. So, I feel you're comment is on the right path (less http requests are better for the client and server), but might not realize that things can be optimized further.
If you want: (i) any chance of all at building a system that works and (ii) happy users, you should codesign the client, protocol and client-facing server if you are providing services to the edge (Web, IoT, etc.)
Inside the data center you can do something else.
The key thing is that REST principles are orthogonal to "a good API", "a successful project", etc.
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.
If "status" is just a property on the accounts resource and doesn't have further meaning, I would tend to agree with you.
If "close" is an action or activity that acts upon an accounts resource, then his approach makes sense.
Since the context is an account that we "need to close," I would assume the author is talking about something more complex than a database field. It's probably a workflow that initiates other actions and workflows, maybe even requiring additional review. I'd want to see/understand the requirements more fully before I started down either path.
More specifically, it shouldn't have to matter to consumers. Because of the way REST and HTTP work, clients intuitively understand retrieving and modifying resources (via GET, POST, PUT, PATCH and DELETE). But they don't understand interacting with special-purpose endpoints (POST always implies "make a new thing" so it's weird in this context).
Your consumers should not have to learn weird idiosyncrasies in your API because you let your data model bleed into the interface.
An API is a representation of business workflows and processes. The more accurately you describe or map those to the real world processes, the better your and your consumers' understanding will be.
It seems like we spend a lot of time designing object relations that map a domain, but maybe not so much mapping domain-specific actions.
Or would you consider all of the above bad practice?
If you're speaking HTML it can be as simple as:
<form action="/close-account?acc_number=12345" method="POST">
<button>Close Account</button>
</form>
Or even: <form action="/close-account" method="POST">
<input type="hidden" name="acc_number" value="12345">
<button>Close Account</button>
</form>
If you're speaking Siren (https://github.com/kevinswiber/siren): {
"actions": [
{
"title": "Close Account",
"method": "POST",
"href": "/close-account",
"type": "application/x-www-form-urlencoded",
"fields": [
{ "name": "acc_number", "type": "hidden", "value": "12345" }
]
}
]
}So what? REST is an API [0]. It's the public interface you expose. What you do behind the scene in your data model is, or should be, irrelevant to the API. Sometimes you'll map your data model practically one-to-one to the REST API, but there are times when I do significant logic in controllers before mapping the result of that logic to a resource. As long as the API is resource oriented and follows good REST practices, it's all good.
0. OK, REST is one way to expose an API, since the wording I used is not amenable to some people.
No, REST is an architectural style.
For APIs. But yes, we can argue semantics now. My point is, REST is a way to expose an API.
Right. Particularly, if a closure request is a thing that has its own status, identity, and associated data elements, which one might wish to examine and interact with (and, while its possible that such interaction might not be possible for external users with privileges only on their own accounts, it might well be possible for internal administrative users), then it makes sense as a resource, rather than a data element buried in representations of another resource.
Without full behavioral requirements and domain model, you really don't know what makes the most sense.
POST /accounts/4402278/close
You would have something like
POST /accounts/4402278/closures
{"rel": "deposit", href: "/account/4502278/deposit"},
{"rel": "withdraw", href: "/account/4502278/withdraw"},
{"rel": "transfer", href: "/account/4502278/transfer"},
{"rel": "close", href: "/account/4502278/close"}This isn't how REST is supposed to work
Fielding's dissertation isn't a spec, which pretty much means everyone can come up with their own little slice of how it should be done and then say they're doing it right.
The true rule of REST: whoever is blogging/commenting about it at the time is doing REST right, all others are confused and incorrect.
Conventionally that is true and it certainly reduces friction in your API by adhering to common conventions.
In the case of closing an account, it depends on what actually happens when you do so, but I would normally have a "status" attribute and use PATCH to update that.
PATCH /users/123
[{ "op": "replace", "path": "/accounts/12345/status", "value": "closed" }]
I personally think that's less readable than the original, but I agree that it seems to more closely fit the REST standard.With the deposit, I don't really understand what's changed here from the original. To me, it just looks like deposit has been pluralised, and everything else is the same:
POST /account/12345/deposits
amount=10Regarding the deposits, the difference is that 'deposits' is a collection of deposit resources vs 'deposit' as a verb. I think in this case it makes a lot of sense to do it that way, as you can then GET /account/12345/deposits and see all the deposits ever made.
POST /accounts/12345/deposits
instead of: POST /accounts/12345/deposit
Perhaps your example would be more obvious if it had different terms, where the noun and verb were more distinct - maybe 'game' and 'play'.Even then, from experience, you end up with some horrible URLs just so it's RESTful. While I try to follow REST, I balance it against making the API readable by human beings.
No, you don't. RESTful applications use URLs as opaque identifiers, and communicate all information via resource representations. Communicating information via resource identifiers is decidedly not-RESTful, so any particular URL structure chosen to communicate specific information in the URL is, ipso facto, not RESTful.
POST /documents/12345/print
To me, while this was not RESTful, it was the most readable way I came up with. When I asked somebody who had more experience with REST than me, he suggested I build the URL like this: POST /users/123/print-jobs
url=/documents/12345
Doing this, I would have to add validation to make sure it was a printable URL. Also, there was no corresponding GET request, so it seemed pointless. And it made the code weirder, as the 'print' action would be separated from the rest of the actions on documents.So how would you do it instead? Or you would do it the same way, and just not worry about the problem?
It seems that consensus on how to do REST breaks down when you have custom verbs.
POST /documents/12345/print-jobs
And you say there's not corresponding GET request, but maybe there should be? Perhaps you'd want to list print jobs, get the status of a particular job, and maybe cancel it?I don't have a print queue because nobody has ever asked for one or shown the need for one.
> When I asked somebody who had more experience with REST than me, he suggested I build the URL like this:
> POST /users/123/print-jobs
What was the logic: neither version is more RESTful, even if you ignore the role of URLs in REST and adopt what seems to be the usual definition of RESTful URLs used by people who use any coherent definition: the URL represents some logical hierarchy of resources.
In this case, your original "POST /documents/12345/print" implies a collection of print requests (I'd probably call it "prints" or "print-requests" rather than "print", but that's a minor quibble) pertaining to a particular document, to which a new request is added when the user wants to print the document. Sounds perfectly sensible.
The alternative offered suggests a list of print jobs belonging to a user, which is also sensible. But there is no strong reason to prefer one over the other.
And if you were actually RESTful, it wouldn't matter, because your resource representation for the print job/request would actually include both the URL and the user. (And maybe status and other information.)
Both your original (with no representation) and the alternative (with the user in the URL and the document in the resource representation) aren't RESTful because they have information that logically belongs to the resource in the URL and out-of-band context rather as part of the resource. But that's not a matter of URL construction, its fine for a URL scheme to correspond to information about the resources, as long as the information is actually still part of the resource.
> It seems that consensus on how to do REST breaks down when you have custom verbs.
If you have real custom verbs, then you may need to extend HTTP's set of methods (it wouldn't be the first time that has been necessary -- PATCH isn't a base HTTP/1.1 method.)
But there is a difference between a generic action on resources of the type represented by an HTTP method and an action-in-the-domain, and it sounds in your case "print" is an action in the domain, which is an event which should have a corresponding noun -- and thus a resource representation -- in the model. But while URLs are inherently hierarchical, real relationships in real domains are often webs, and so there's not one true way to map it into a hierarchy.
One thing that helps is not nesting resources too deep, to avoid having really long URLS.
POST /transfer
from_account_id=12345&to_account_id=67890&amount=10
Perhaps I've misunderstood your comment, as I don't really understand why this is better than the original: POST /accounts/12345/transfer
to_account_id=67890&amount=10
or even: POST /accounts/12345/withdraw
amount=10The key point though is that 'transfer' is the resource to perform all three of the original deposit/withdrawal/transfer actions.
Of course its right. Read the POST spec. POST is for processing data. Its up to the server to what is processed how. If the POST is for closing a bank account, it is valid. I think, my bank would need verify a lot of things before I can close my account with a single click. So the operation cannot be idempotent and thus requires a POST.
I mean, if you are fine with setting just a flag in your bank, you can be fine with a PUT. But I will not become a customer of your bank.
Again, this is the interface to a complex data model, and I would be wary of using a bank that dumped all of its security and process controls into one endpoint's controller.
Also - closing an account is an inherently idempotent operation, no? It can only be closed once. If I request that a closed account be closed again, it stays closed.
"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
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.
REST APIs must be hypertext-driven. http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...
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.
If you think this is possible in the wild or not is another matter.
*each nugget of data can be versioned and meaninged independently
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.
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
Shoehorning HATEOS in JSON APIs is a category error:
http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.ht...
Long story short, REST: you are all doing it completely wrong.
- 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.
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.
bullshit. POST should not contain all details in URL. POST can have body and each query should not be unique. Stopped reading after that.
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).
PATCH /accounts/4402278
with parameter ACTIVE: 0
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.
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..., http://cafe.elharo.com/web/why-rest-failed/, http://www.artima.com/lejava/articles/why_put_and_delete.htm..., 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.
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.
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!
It WOULD make a nice set with CRUD operations:
POST - Create
GET - Read
PUT - Update
DELETE - Delete
But that's not actually how the HTTP verbs are defined[1]. Instead, my own mental mapping looks something like this:
GET - read information. Must be idempotent and side-effect free so this one really IS just for reading.
DELETE- delete. Usually pretty obvious except for arguments about whether a "soft delete" (mark as deleted but don't remove from the underlying DB -- like a bank account which can be closed but continues to exist (its account number, for instance, is never freed up).
PUT - update things. Must be idempotent. If you're a stickler for doing things right, this should NOT be use for a "partial update" (where you supply a few fields and everything else is left "as is") but only for a "total update" (where the new thing you sent in replaces whatever was there).
POST - "do it". Means to do the obvious thing (whatever that is for this URL).
PATCH - great idea. Maybe in a few years support for it will be ubiquitous enough that we can actually start using it. If we DID us it, this would be the partial update variant of PUT.
OPTIONS and HEAD - occasionally used at a framework level. You won't ever use it.
everything else - don't use it.
[1] - https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Similarly a PUT should make sense whether the underlying resource 4XXes or 2XXes, and should in either case make it 2XX with the same response.
Then POST is just a verb which does not share these semantics at all; POST <url> can be done on a URL which either 4XXes or 2XXes, and even if the post succeeds that's no guarantee that the underlying URL will now exist. POST /update-caches for example might not change the HTTP statuses of anything.
Also, there is nothing wrong with having an idempotent POST effect. "POST is not supposed to be idempotent" is not the same as "POST should never be idempotent". In this case, the POST acts on a boolean flag; it is harder to make it not idempotent.
To do this in 100% RESTful style you would therefore probably store details about how an account was closed in the /accounts/440278/closed reference, so a PUT is correct, with GET /accounts/440278/closed returning either a 404 for accounts which are not closed or else the metadata about how the account was closed. This could live in parallel with a cache that's on the resource itself describing whether it was closed or not.
So, POST or PUT, with the PUT exposing more of the metadata interface to REST and POST putting more of it in the request bodies themselves.
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.
If everyone has experience with similar systems, then you're fine. If others have worked with hard delete systems or aren't native English speakers, you're introducing ambiguity and context that isn't necessarily there.
So you can call DELETE on an account then your next GET returns a 404 because you deleted it from the database, or you can call DELETE and your next GET returns a 405 because the account has been closed and you're not allowed to call GET on a closed account. Both are totally within the spirit of what DELETE is supposed to accomplish, and I'm sure there are a half dozen other ways to architect it completely within the spirit of DELETE and REST as well.
Given that they don't, really, I think POST isn't terrible. At least it's not GET!
Similarly, POST is not idempotent because it modifies (part of) the resource's state while leaving it otherwise untouched.
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" }`- API Anti-Patterns: How to Avoid Common REST Mistakes [1]
[0] https://www.infoq.com/articles/rest-anti-patterns
[1] http://www.programmableweb.com/news/api-anti-patterns-how-to...
Question 2#: What is the consensus around custom verbs? While I've never gone down this route, a few times I have been tempted.
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).
https://jacobian.org/writing/rest-worst-practices/
Every web developer should read that ^
You should use POST /session instead.
I think a JWT token represents a session, since it can expire and be disposed when logging out.
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
The front end team wanted something more like this (before it was ensconced in an RFC): https://tools.ietf.org/html/rfc7386
To me, RFC7386 more accurately represents the idea of just sending the updated content.