
REST Anti-patterns - marcelocure
http://marcelo-cure.blogspot.com/2016/09/rest-anti-patterns.html
======
PaulHoule
This 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.

~~~
robzhu
You might want to check out GraphQL:
[http://graphql.org/](http://graphql.org/). One of its killer features is the
ability for clients to specify exactly the data it needs and obtain it in a
single request/response.

~~~
EuAndreh
Also checkout David Nolen's "Clients in Control"[0]. He approaches
specifically those points mentioned, and pilfers ideas from Relay and Falcor.

[0]: [http://www.datomic.com/videos.html](http://www.datomic.com/videos.html)

------
fzilla
Other 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.

~~~
caseysoftware
This depends on your data model.

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.

~~~
fzilla
REST is just a representation of the underlying data. What does it matter if
"status" is a database field or not?

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.

~~~
amk_
What if 'status' isn't a column in the account database? What if
closed_accounts is a join table or something else? Then wouldn't adding a
/close route be _hiding_ idiosyncrasies in your data model, not the other way
around?

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?

~~~
jnbiche
> What if 'status' isn't a column in the account database? What if
> closed_accounts is a join table or something else? Then wouldn't adding a
> /close route be hiding idiosyncrasies in your data model, not the other way
> around?

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.

~~~
dragonwriter
> REST is an API.

No, REST is an architectural style.

~~~
jnbiche
> 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.

------
macca321
How 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-
hyperte...](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-
driven)).

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

------
niftich
The 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
_HATEOAS 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-hyperte...](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-
hypertext-driven)

------
barrkel
HATEOAS 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
The 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
That, and the semantic web, and pluggable components from different vendors
using standardized interfaces, and other architecture astronaut ideas of
software composition. Any day now.

------
carsongross
The 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.ht...](http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.html)

Long story short, REST: you are all doing it completely wrong.

~~~
Falkon1313
The 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
Without agency on the client side, supplied by humans, how is it advantageous
to send an unknown number of potential actions from the server?

------
vinceve
What 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.

~~~
idbehold
There's an RFC for that!
[https://tools.ietf.org/html/rfc7807](https://tools.ietf.org/html/rfc7807)

------
beat
These aren't really antipatterns. They're just bad design. Not all bad design
is an antipattern.

~~~
blowski
Much sexier and authoritative sounding name, though.

------
EugeneOZ
"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
I 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).

~~~
EugeneOZ
"close" is a verb, so it's not a resource, it's a method and 4402278 is just
argument of that method.

------
KillerRAK
Resource 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.

------
jack9
> 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...](http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),
[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.htm...](http://www.artima.com/lejava/articles/why_put_and_delete.html),
[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.

------
tvjames
Useful 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)

------
orware
I'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!

------
mikio
Why 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

~~~
mcherm
You may be operating under the misapprehension that "POST" means "CREATE".

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](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)

~~~
drostie
I like to think of DELETE as "if the response to the DELETE is 200-level then
further GETs to the resource should be at the 400-level." Call those responses
to GET requests "2XXing" and "4XXing" respectively.

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.

~~~
mcherm
That's a nice simple view of it, I really like it.

------
marcelocure
right, 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
Strictly 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.

------
laurent123456
> 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" }`

------
0xmohit
\- 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...](http://www.programmableweb.com/news/api-anti-patterns-how-to-avoid-
common-rest-mistakes/2010/08/13)

------
whamlastxmas
Disappointing 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
Question 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
Re #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).

------
SEJeff
Also 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 ^

------
jackcosgrove
No mention of the biggest single problem with REST as it's practiced: POST
/login.

You should use POST /session instead.

~~~
rahkiin
What if i use JWT and thus have no concept of 'session' resources? You would
actually create a token using username and password.

~~~
jackcosgrove
My 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.

------
nmgsd
Nice to PATCH mentioned. Highly useful and often overlooked.

~~~
strictnein
Yes. 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.

------
currywurst
Are there good guidelines on complex patterns in modeling your resources ? E.g
long-running operations involving multiple entities.

------
partycoder
Cache control and misuse of e-tags is also a common problem.

