

Designing a RESTful web API - scotchio
http://scotch.io/bar-talk/designing-a-restful-web-api#reader-mode

======
slashdotaccount
This article does not describe a RESTful architecture. Several things that do
not pass the Litmus test <[http://roy.gbiv.com/untangled/2008/rest-apis-must-
be-hyperte...](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-
driven>) or otherwise stick out like a sore thumb:

1\. Explicit versioning, stuffed into each resource's URI

This is really bad, it shows the designer has only experience with RPC, but
not resource-oriented. Bumping the server version breaks all clients for no
good reason. Imagine if the Web sites worked that way: you have to upgrade
your browser before you can visit an updated site. The correct way to do it is
to version gradually, and express a changed semantic for a resource type by
coining a new link relation for it. Do not put semantics in a resource URI as
the article advises!

2\. Utter lack of hypermedia controls

The article mentions CSV as possible media type for that entity. As we all
know, CSV does not have or support hyperlinks, forms or any other hypermedia
control. This is a serious oversight because it's not possible to have a
RESTful architecture without those controls. Remedy: the designer must
implement controls in the message body where natively supported by an entity
type (e.g. XLink or XForms for XML) or else fall back to the message header
(e.g. Link header/URI templates for CSV).

3\. Fixed URI structuring

This is related to point 2 above. Example:
</v1/stores/near?lat=12.34&lon=-12.34> How does the client know how to
construct this URI? The article mentions no hyperlinks or other controls. It
does say "define the right endpoint names" which I surmise go into some sort
of specification document, which is wrong. Doing so makes the server lose
control over its URI space and creates unnecessary tight coupling, which is
brittle. Express typed resources with link relations, not with the resource
URI structure! There must be only exactly one entry point (bookmark) for the
client to know, all other URIs are derived by traversing the resources through
controls!

4\. Lack of clue about security

The article talks about authentication with OAuth or HTTP basic
authentication. OAuth is chiefly for 3rd party authorization, no wonder the
article's author is confused. Implementing this is workable, but overkill if
all you need is authentication, making it much more difficult for a programmer
to implement client software correctly, but brings no benefit. HTTP basic
authentication is either clear text or susceptible to replay attacks.
<[http://www.xml.com/pub/a/2003/12/17/dive.html>](http://www.xml.com/pub/a/2003/12/17/dive.html>)
Remedy: it's 2014, just use TLS. Let the transport layer take care of
authenticating a user, the application code on the server is free of
authentication.

5\. Reinvents the wheel for error handling

Established draft standards exist. Use them.
<[http://www.mnot.net/blog/2013/05/15/http_problem>](http://www.mnot.net/blog/2013/05/15/http_problem>)
<[https://github.com/blongden/vnd.error>](https://github.com/blongden/vnd.error>)

tl;dr Article is pretty much amateur hour, I advise the interested reader to
get one of the classic books on REST instead to learn it properly.
<[http://oreilly.com/catalog/9780596529260>](http://oreilly.com/catalog/9780596529260>)
<[http://oreilly.com/catalog/9780596805838>](http://oreilly.com/catalog/9780596805838>)
<[http://oreilly.com/catalog/9781449358068>](http://oreilly.com/catalog/9781449358068>)

Update: angle brackets are reserved characters for delimiting a URI, not part
of a URI. Hackernews, you are out of compliance with RFC 3986, please fix your
shit.

------
arethuza
One the subject of versioning, one thing I rather like is to make the versions
resources withing the API itself so you really can stick to the RESTful rule
of clients only having to know one URL.

e.g. For Salesforce's REST API you can see all the versions at:

[http://na1.salesforce.com/services/data/](http://na1.salesforce.com/services/data/)

Documentation:

[https://www.salesforce.com/us/developer/docs/api_rest/](https://www.salesforce.com/us/developer/docs/api_rest/)

~~~
thecodemonkey
(Author of the article here)

That's a great point! But, wouldn't you need to update your code anyways when
migrating to a newer API version? The idea is that you only release a new API
version for backwards-incompatible changes. E.g. when we launched fields for
Geocodio[1], all you needed to do was append a "&fields=x" parameter to your
query string, so that didn't require a new API version.

So I'm not sure what the exact purpose of getting the version number as a
resource would be?

[1]
[http://geocod.io/blog/2014/04/02/fields/](http://geocod.io/blog/2014/04/02/fields/)

~~~
arethuza
I guess I just like the elegance of it - but thinking about it a bit more, it
means that it is easier to configure a client.

If I install a client application where there are multiple implementations of
the same service (not the case with SFDC) I just want to have to know the URL
for their _service_ not to have to work out which version of the service this
particular application works with and which are available - the application
should be able to do that.

~~~
abrichr
This doesn't seem like a particularly scalable solution, since your traffic
increases proportionally to the number of versions you support.

~~~
arethuza
I would suggest that if it works for Salesforce then it probably does scale...

~~~
abrichr
Perhaps, but I would prefer to see some evidence rather than rely on an appeal
to authority.

If the size of your resource is e.g. 100KB, and you have 10 different
versions, then your responses are 1MB each -- 9 times larger than they have to
be. This can result in significant bandwidth costs, depending on your number
of users and their behaviour.

~~~
arethuza
I think you are misunderstanding how it works - you don't get every possible
response format, that would indeed be silly. You get the response type
specific to that version of the interface - which is pretty normal versioning.

All I was saying is that if you do run multiple versions of the same interface
on different URLs then it's quite nice to have these versions themselves
treated as resources you can find out about through the interface. That's what
Salesforce do and, in my experience, I think it is simple and elegant.

~~~
abrichr
Ah, now I understand. Yes, I agree that discoverability is an extremely useful
feature.

------
jksmith
Dig the article. A couple of things. 1) I can see what you mean for handling
auth keys in query string as opposed to "key:" using basic auth. I started
using the latter pattern after I went through easypost's api docs. Even though
"key:" is a little unbalanced I guess I still dig the basic auth way. YMMV. 2)
On error messages, I wrote some code to provide extended info only if the req
host is in a white list (set up your white list in an ini file or whatever). I
was originally just throwing back a 500 plus whatever the error returned, but
the db error info returned by some of the golang drivers is detailed enough
that I didn't want to share all of it. Just curious if you had noodled on this
issue if it is one for you.

~~~
thecodemonkey
Thanks! Hopefully 500 errors would never occur of course, but in that case I'd
just return: { error: "Internal server error, please try again later" }

There's not really a reason to give away anything else, it's usually because
something is broken (database connection and whatnot) and the developers are
probably already aware of it with tools such as Bugsnag[1] and alike.

In general for error messages, exceptions should be handled gracefully and
output a simple message that is understandable for the user. It might also be
ideal to include an error code with the JSON response if you have a lot of
different error messages that happens under the same HTTP status code (e.g.
400 Client Error).

[1] [https://bugsnag.com](https://bugsnag.com)

~~~
jksmith
Cool. Thanks for that. Yeah that's a good thing about golang. No exceptions
(levity alert)! So it's always a message you get back unless somebody hit the
panic button.

------
abrichr
A couple of notes on Authentication:

\- Including the clear text API key in the URI makes it very easy for anyone
listening in to gain unauthorized access. To get around this, one can either
enforce HTTPS, or require a hash of a pass phrase.

\- Another option to look into is Kerberos.

~~~
thecodemonkey
(I'm the author of the article) HTTPS is definitely recommended. I would
however recommend against using a hash of a pass phrase since it would have
the same effect as an API key (you would still be able to replay it), and we
all know that hashes without salts never go well :)

~~~
feralmoan
JWT is an interesting standard ([http://tools.ietf.org/html/draft-ietf-oauth-
json-web-token-1...](http://tools.ietf.org/html/draft-ietf-oauth-json-web-
token-19)) dealing with sessionless temporal auth tokens that offers some
flexibility between http basic (native) or signed tokens using the same
credentials. If you're not using TLS you're kind of screwed anyway, whatever
the auth scheme!

------
dickeytk
Good article. On the authentication note thought, oath should only be used if
you have partners that need to access the API on user's behalf. If the API is
internal it's overkill.

I prefer single token methods like jwt.

~~~
thecodemonkey
(Author of the article here) I agree that OAuth can be a little bit
complicated to deal with for some uses cases, if the API is internal you'd
likely have an external firewall anyways so that makes good sense.

