
Designing a RESTful API That Doesn't Suck - hunvreus
http://devo.ps/blog/2013/03/22/designing-a-restful-api-that-doesn-t-suck.html
======
buro9
Time, content types and showing the developer (and user) what's possible
(HATEOAS)...

 _Time_

Using UNIX time may be easier for a developer, and I do concede that point,
but by using UNIX time you're doing a few things:

1) You're exposing some internal logic or data structure of the system in the
resource

2) You're removing information on time zone

3) In some circumstances where greater precision is needed you're precluding
the use of nanoseconds

I know that some languages don't naturally handle ISO8601 dates with
nanoseconds, but I do think that's the best format.

YYYY-MM-DDTHH24:MI:SS.NNNNNNNNNZ

With that format one can keep a deeper knowledge of timezone, of precision, of
human readability (and JSON.org does say that JSON is easy for humans to
read).

 _Content Types_

Whilst it may be pragmatic to not plumb the depths of custom content types
they should at least be mentioned and made compulsory even if they aren't
typed to a detailed level of specificity. Content Type: application/json is
fine, so long as it's used and paid attention to.

 _Capabilities_

HATEOAS and meta-data... I like to encourage developers to explore what's
possible in the API by showing them the actions that they can do on an item,
and their permissions. As in... if they can update something, show them how by
telling them the URL. The API should ideally not lead a developer to an action
that they couldn't perform.

 _Minor Points_

I generally prefer to restrict the possible HTTP status codes and publishing
that list so that a developer implementing against the API can build a generic
wrapper, know what to expect and take care of handling it. Which means,
sometimes I will generalise a response rather than using a status code in one
place and one place only.

I'm not so sure on using verbs for special actions like search, 'q' is a well
understood part of the 'query string', and I'd make that just an optional
parameter on the pluralised collection URL. But... perhaps that's just a bad
example.

~~~
BerislavLopac
On your "Capabilities" point, if it's REST you don't need to tell them the
URL. With REST, the URL for a resource never changes.

~~~
pilgrim689
> With REST, the URL for a resource never changes.

I could have an endpoint example.com/users become example.com/subsite/users.
It is precisely _because_ the URL might change that you want to give the
client URLs to the next possible actions.

~~~
BerislavLopac
Er, but no; you're very wrong.

First, yes, you could change your "endpoint" URL, but that's not the reason
for providing URLs in HATEOAS (just for the record, I hate(oas) that acronym).

You see, REST is different from most other Web services because it doesn't
provide access to _actions_ , but to resources. It can be compared to the good
old SQL database -- and a resource's URL is the way to uniquely identify that
resource, just like in a RDB you would have a combination of a table and a
unique identifier.

The reason for providing URLs is to free the consumer from having to construct
it by hand each time. In fact, an URL can change -- but not an URL to a single
resource (just like you wouldn't arbitrary change a RDB record's ID); rather,
a completely different resource can be provided instead.

~~~
pilgrim689
I misused "action". Replace "to give the client URLs to the next possible
actions." with "to give the client URLs so they can perform their next
possible actions on related resources".

My point remains: one of the main reasons of using HATEOAS is so that you can
evolve your API without breaking clients. The other main benefit is so that
your client can discover your protocol by simply following hypermedia
controls.

------
zedr
Encoding the version number in the URL may be convenient, but certainly does
not follow REST. Consider the following pair of URLS:

    
    
        /api/v1/posts/1
    
        /api/v2/posts/1
    

This implies out-of-band information that these two resources are actually the
same entity.

The version number belongs in the `Accept:` field of the HTTP request headers;
for example `Accept: application/json; version=2`

~~~
mnarayan01

      Accept: application/json; version=2
    

Is that a standard-compliant media type? `version` doesn't appear to be
associated with application/json in the IANA media type registry, and I
couldn't find anything definitive about adding arbitrary parameters.

~~~
masklinn
I'm pretty sure parameters are not limited to those provided by IANA (just as
media types are not necessarily IANA-registered incidentally) e.g. RFC2616's
Accept doc shows a `level` parameter to text/html which is not IANA-specified
(and seems entirely unspecified)

~~~
mnarayan01
Yea I saw the use of level in the example but didn't think to look at the
text/html spec to see if it was there. That would seem to imply that you are
correct (though it would be nice if there were an explicit answer on this, if
only for purely OCD reasons).

> just as media types are not necessarily IANA-registered incidentally

Per RFC4288 I believe they must be if they are in the standard tree (i.e. the
name does not contain a '.' or an 'x-' prefix for historical reasons).

------
stuffihavemade
How about the most important concept, <http://en.wikipedia.org/wiki/HATEOAS> ?

~~~
hunvreus
I was planning on diving in deeper in my next post. This was more of a quick
and practical list of things you should follow for your API not to suck too
much.

But yeah, I agree.

~~~
shintoist
Would love a practical HATEOAS point by point write up like the posted
article. Nicely written

------
jrochkind1
agree with everything except... unix time, really? Unix time has all sorts of
weird potential problems, including year 2038, historical inconsistent
handling of leap seconds[1], more.

ISO8601 or bust!

Other than that, yes, I think there are things about REST and REST-like API's
that are not clear and subject to debate about best way to do it -- but none
of them are mentioned in this post, these are the floor, yep.

[1]<http://cr.yp.to/proto/utctai.html>

------
AznHisoka
What's a RESTful way of authenticating users when you will also have a web app
that will use the same API? Should there be just OAuth for API users, and
cookies/sessions for web users, or is there a way to make both use the same
authentication method?

~~~
julien_c
Why don't you use OAuth for your Web app as well?

------
yuchi
I still don't get why we're using the API version _in the url_. We should use
the specialized content-type instead!

~~~
mfenniak
Because that approach isn't recommended anymore. In short, it's much harder to
for a cache to work effectively based upon Accept & Content-Type headers than
it is for it to work on the URL.

For example, here's a quote from Roy Fielding: "In general, I avoid content
negotiation without redirects like the plague because of its effect on
caching.". In other words, content negotiation for a versioned content-type is
OK... if it redirects to a URL that contains the version.

httpbis, the IETF working group in charge of maintaining and developing the
core HTTP spec, has deprecated content negotiation:
<http://trac.tools.ietf.org/wg/httpbis/ticket/81>; it's still part of a
separate spec (RFC2295), but it's not a core component of HTTP anymore. Why?
'HTTP content negotiation was one of those "nice in theory" protocol additions
that, in practice, didn't work out.'

Certainly you can still build an API that works fine using specialized content
types. Versions in the URL are a pretty practical approach that work very well
too.

~~~
mudetroit
Having admittedly not descended very deeply into the rules regarding HTTP
caching, why should it be harder to cache based on the cross-section of the
URL and the Accept-Header and not simply upon the URL?

I am not certain which method I prefer more honestly, and could fall either
way. The comment that it was nice in theory but didn't work in practice seems
to more point to not following the spec as it was written then a problem with
the spec itself.

~~~
mfenniak
I would say it's far more difficult if you want to support the full range of
capabilities that the Accept header supports. Accept can support more than a
single mime-type, and can also include priorities on each mime-type accepted.
If you combine that with multiple clients, I think caching based upon that
header would be pretty difficult (or you'd have to make some simplifying
assumptions).

Impossible? No, not really.

Worthwhile? My opinion is no. I haven't seen the practical benefits of the
content-negotiation approach over the URL approach, but I have seen the
practical difficulties, so I'm convinced.

------
masklinn
Discussed:

* HTTP verbs

* status codes

* look of URLs

* error messages

Not discussed:

* content types

* hyperlinks

Conclusion: not about REST or RESTfulness, guide about RPC-over-HTTP.

~~~
untothebreach
Not to mention that JSON (by itself) is not sufficient for hypertext

~~~
masklinn
Naturally, but that's covered in "content types". Also you could always use
LINK headers with whatever @rel, depending on what you're linking or how.

------
brown9-2
I've found that it is very helpful to include some sort of pseudo-unique
"Incident number" in error responses which you also log in the server's log
along with any exception/error details.

This makes it very easy then to correlate issues reported by users with the
actual errors/requests in the log.

------
thelarry
Every few months people start an argument about post vs put vs patch and
versioning in the url vs some header. I think a lot of developers aren't fully
aware of PATCH and making put actually fully replace an object can be
dangerous. This probably especially bad if you use some schemaless db. For
versioning, I like to support both. Let the user do what makes them happy. Not
really that hard to support URL and header...

------
zedr
Isn't a Web site also a Web service? Following REST, should't the Web site and
API be unified, with the same resources being consumed by both humans and
robots (by varying the representation)?

I posted this question on Stackoverflow a few weeks ago:
[http://stackoverflow.com/questions/15360415/should-a-web-
sit...](http://stackoverflow.com/questions/15360415/should-a-web-site-also-be-
a-web-resource)

------
bothra90
Another article where the author thinks(confuses?) REST and CRUD have a one-
to-one mapping?

Ref: [https://jcalcote.wordpress.com/2008/10/16/put-or-post-the-
re...](https://jcalcote.wordpress.com/2008/10/16/put-or-post-the-rest-of-the-
story/)

------
gbog
I wonder if there is some convention for automatic api exploration. Would be
nice also for auto documentation.

~~~
gamache
There is -- hypermedia. That's the practice of driving your API by following
hyperlinks, rather than by constructing URLs and requesting them.

There are several schemes of encoding link information in API resources. I
like the Hypertext Application Language, or HAL, the best. Check it out:
<http://stateless.co/hal_specification.html>

~~~
byroot
The Github API started to implement it but is now going backwards[0]:

    
    
      Standardize on existing *_url attributes for hypermedia. Remove all _links objects
    

If somebody knows the rational behind it I would love to hear it.

[0] <http://developer.github.com/>

------
polack
POST for update and PUT for create, not the other way around.

EDIT: Wow. A lot of people who think I'm wrong. I agree with you that its not
the whole truth, but I think its more true (for most cases) than what the
article say.

~~~
drdaeman
PUT for creating and complete updating, PATCH for partial updates.

POST has no useful semantics (more correctly, its semantics is just like those
of "do"/"execute" verb) and should be used only if no other verb matches. Or
for compatibility reasons ("POST /foo\nX-HTTP-Method-Override: PUT")

~~~
dragonwriter
> POST has no useful semantics (more correctly, its semantics is just like
> those of "do"/"execute" verb) and should be used only if no other verb
> matches.

While POST is often used as a generic "do"/"execute", its actual defined
semantics in RFC2616 are for the server to "accept the entity enclosed in the
request as a new subordinate of the resource identified by the Request-URI in
the Request-Line."

So, aside from fallback uses, its explicitly the correct verb to use for a
request that is intended to create a resource where the client doesn't know
the identifier of the resource to be created, only its parent. This is
particularly likely to be the case anytime the resource to be created is of a
kind that will have a server-assigned key that will be part of the URI.

