Hacker News new | comments | ask | show | jobs | submit login
How RESTful is Your API? (bitnative.com)
97 points by housecor on Aug 27, 2012 | hide | past | web | favorite | 56 comments



I've written probably half a dozen unrelated apps that call REST APIs and I think they all implemented REST differently. Some treat PUT as insert while POST as update. Some treat POST as insert, PUT as insert/update. Some return 201 when insert was successful, some return 200 whenever anything is successful. Some issue tokens, some authenticate using headers.

Just last night I wrote some code to auto-create Trello cards and assign them to a specific list. The https://trello.com/docs/ seem fairly decent but what they don't tell you is that POST is insert, PUT is update. Moreover, while you can assign a due date via PUT ( https://trello.com/docs/api/card/index.html#put-1-cards-card... ), you cannot assign one via POST ( https://trello.com/docs/api/card/index.html#post-1-cards ). So if you want to create a card with a due date, you have to POST, read ID, then PUT the due date to /card/id.

The problem with people trying to be RESTful is that they pay too much attention to stuff that doesn't matter (71 status codes) instead of stuff that really matters (simplify complex insert/update). I'd rather have Trello return 400 on errors and provide a text description than use one of the 30+ 4xx errors. And I'd rather have them accept due date when inserting a card. Trying to conform to REST's standards is like trying to be XHTML compliant. Great, your site validates. But you're still using white font on yellow background!

Be as RESTful as you need to be. But more than that, be useful and sensible. Don't make me call your server six times to make one valid GET request.


"POST is insert - PUT is update" is the norm and HTTP compliant way of doing things. Why not reach out to Trello and ask why you can't do the due-date on POST?


Unfortunately, I don't think the POST vs. PUT distinction is quite that simple - PUT is often used to create/insert if you are sending all the data required to create the new object as part of the request.

NB Personally I thought this use of PUT was splitting hairs when I first started reading about RESTful interfaces but having used a few and written one RESTful service it does rather make sense.


PUT should be idempotent and addressed at the location of the resource to be created / updated. e.g /books/[isbn], while POST should be addressed at the location of the collection and give the location of the new resource or an error. e.g. POST to /books to create a new book entry.


That's still an oversimplification. POST only implies that the request is NOT idempotent, and that the request body is submitted to the origin server for "processing". Consider for example, resources that search for other resources via information in the request body and redirect to them. There's nothing wrong with this pattern, and in fact it works nicely for rapidly-changing server-side information since caching the result of a POST is not allowed.

The core problem in my mind, is not whether one should insert or update on PUT/POST, but whether one should even be considering HTTP-based APIs to be gateways to what amounts to a database. The domain concepts in the application might not map directly to a resource-centric design, and that's ok. When you focus on the resources, their behavior, and hypermedia, then you "get" REST. If your application isn't shaped like HTTP/REST, then maybe it shouldn't try to be.


> The core problem in my mind, is not whether one should insert or update on PUT/POST, but whether one should even be considering HTTP-based APIs to be gateways to what amounts to a database.

I think it's widely recognized that you shouldn't consider making your REST API a direct gateway to a database. For one, it's potentially very dangerous, but it is also limiting the design of your API, because typical databases basically have no way to insert anything besides rows of data with fixed columns (relational) or raw untyped data (NoSQL).

I think the 'purest' form of REST API's for creating and updating is that PUT should be idempotent and used for existing resources addressed by an existing URI, while POST can basically create resources any way it sees fit, and return the URI of the new resource. This allows doing a POST followed by a PUT to create and initialize a resource, without having to know how and where the resource will be created, and without requiring to set each and every attribute of the resource with the POST request.


Exactly, that's why the POST/PUT distinction makes perfect sense when you actually write code but seems a bit academic when you read most descriptions of how RESTful interfaces should work.


unless you're the one maintaining the api and the calling code doesn't/won't/can't distinguish between insert and update because the de-dupe algorithms are not up to snuff or the current financial year doesn't have budget to get the third party feeding your api to re-develop it.....

bitter? me? nah


POST is perform, PUT is replace or set.


I'm surprised nobody has mentioned the Richardson Maturity Model: http://martinfowler.com/articles/richardsonMaturityModel.htm.... It's explicitly for discussing and characterising the degree of RESTfulness of a given API.

For my money there's nothing actually wrong with implementing, say, a level 1 API, as long as you don't claim anything higher. It'd be very nice for everyone to be at level 3, but obviously the tooling isn't there yet to support it universally.


I hadn't seen it before, but I like it. Practical and specific. I think you could actually use this when proposing time/cost estimates for web service development.


that is a great article. makes a strong argument for the point of hypermedia links too.


There is much ado in the REST world about whether one API is more restful than another. For us, our REST API was designed from our experience as consumers of other REST API's. Our whole philosophy wasn't about what was true-rest but rather, what was both consistent and easy to learn.

We do fancy stuff like send back HTTP codes that aren't always invited to the cool HTTP code parties like, "Payment Required" and we used "Accepted" on a PUT (Update).

Also, while we know that a rest API should be entirely self documenting we found it more practical to create a GitHub account explaining every call with sample code in variety of languages, all doing the same thing: sending JSON via a REST library.

We are a B2B product and reducing any barriers to enriching our clients is a must. We've worked on big dev teams and understand that if we want our product to get on the next sprint we need to make sure that they can copy and paste our code into their software and it'll work. It's just the nuts and bolts of business.

I would accept data on floppy disk duct taped to carrier pigeons if that was the easiest way for clients to interact with our algorithms. Fortunately for us, being "pretty" rest-y was a better fit for everyone.


> Why Deviate? > Although REST prescribes using HTTP GET, PUT, POST, and DELETE verbs for CRUD operations, some clients can’t generate the less common PUT and DELETE requests. In addition, some overzealous firewalls block PUT and POST. Thus, some RESTful APIs accept all requests via HTTP GET and place the HTTP verb in the querystring. For example, to delete user 124, the GET request would be for the following URI: /users/124?method=delete This technique is especially common in Ruby circles. Another benefit of this approach is all requests can be easily generated and manipulated in the address line of any browser.

This is a very bad idea for something like delete. It's Daily WTF material:

http://thedailywtf.com/Articles/The_Spider_of_Doom.aspx

Any firewall that blocks POST requests but not GET is a WTF in and of itself, for that matter.


Some people resolve this with the X-HTTP-Method-Override header which I believe is a lot more clean. This is very helpful in situations where certain strict REST clients don't support PATCH (the underrated verb to help with partial updates).


Overloading the request method using URL paramaters, as the author mentions is a bad idea. What is accepted behavior, is overloading the request method using hidden form data in a POST request.

The later is what Rails actually does - http://guides.rubyonrails.org/form_helpers.html section 2.4

"There are two noncontroversial uses for overloaded POST. The first is to simulate HTTP's uniform interface for clients like web browsers that don't support PUT or DELETE" - RESTful Web Services, Leonard Richardson & Sam Ruby.


This I agree with. But that comes a lot closer to honoring the semantics of the HTTP verbs, too. I do understand the need to be flexible sometimes, especially when some things aren't well-supported, but I cringe whenever I see GET requests changing application state.


Great point Natsu. I agree and have edited my post accordingly.


> Notice what this doesn’t include? XML and JSON. Neither offers a native way to convey a hyperlink.

Both XML and JSON offer standard ways to include hyperlinks. XLink[1] and JSON Schema[2], respectively. Not to mention you can include hyperlinks in headers[3].

The problem with “hypermedia” is that the concept is completely useless without common nouns and verbs. So it’s fine if you can fit your application to use WebDav or AtomPub plus extensions, but for a custom API there’s no point.

[1] https://en.wikipedia.org/wiki/XLink

[2] http://json-schema.org/

[3] urn:ietf:rfc:2068 (https://tools.ietf.org/html/rfc2068#section-19.6.2.4)


I found the cheat sheet annoying. Doing all requests over GET is not acceptable, especially deletes. The jury is still out on GET/POST vs GET/POST/PUT/DELETE. I don't see any problem with file extensions. After all, that's how the web has worked from the very beginning. Hard to do discovery without any guidance on format. Version numbers are not very RESTful and I don't care for them. Spend a little extra design energy to avoid the need for versioning. Static URLs are sort of necessary absent better discovery mechanisms. He skips the notion of returning a resource address as a "Location" upon creation.


Annoying or not, its a fairly accurate description of the state of APIs. I agree about the all GET setup, although I've never actually seen that in the wild (seems pretty dangerous). I personally have no problem with using POST for all endpoints with side effects, though opinions certainly differ there.


Because a URL defines one resource in REST, so strictly speaking, /path/resource.json and /path/resource.xml are different resources using REST.

Content-negotiation is what people should use to specify format.


pbreit - Point taken on using only GET. I have edited the article accordingly.


Dramatic, and makes broad unsubstantiated claims about API's "in the wild", but the details are right. His most important point that we as API writers can work on is the "discovery" part. Hardly anyone is doing HATEOAS [1]. I think the reason for this is that if you write a public API, you're going to document it well (or nobody will use it), and good documentation kind of alleviates the need for in-service discovery.

[1] http://en.wikipedia.org/wiki/HATEOAS


I still don't see the value proposition for HATEOAS. Writing a client that can use an API that it doesn't understand seems impossible, so I can't see how discovery is ever going to work. To my mind HATEOAS represents the very complex over-engineering that REST was originally a reaction against.


I only recently properly grokked this. Discovery isn't necessarily the point, although it's nice. What makes HATEOAS essential for me is that if you start with the assumption of HATEOAS, you can add functionality in the form of new links at the server side without breaking existing clients. Yes, there are other ways to achieve this, but HATEOAS is an excellent way of getting it for free.


You can add new functionality by adding new endpoints without using HATEOAS too. The only difference is that it's documented out of band, which incidentally, is primarily a discovery-related concern.


Exactly. It's not just the existence of the endpoints, though. It's also having developed the client from the first release to be HATEOAS-aware which makes the whole thing interesting. If you do that you'll have conventions in the client code which, even if you're not doing automated discovery, will make it easier to add functionality to a new release.


Write most client code in a generic way so that it's easy to add new actions? Sure.

Write the client to use new actions without needing an update? Impossible.

Output the list of available actions using <link rel="..."> rather than some domain-specific tag? What do I ever gain from this other than bloating my API and making it harder to read?


In some cases, client code could be written so that new actions can be discovered and utilised without any changes to the client code. There have been many cases when I've written code that renders things, and I've reused that code to render multiple different things. Eg I might have a page that lists blog posts, and another page that lists comments on a blog post. Some of those things can be deleted, some of them can't, but the code to delete is there for both of them, it's just that on the ones that can't, the delete button is hidden. Using HATEOUS, if the server did start supporting deleting something, that could automatically detect that, and automatically support that new feature that the server now supports.


Sure, use a domain-specific tag. As long as it contains an href, you're still doing HATEOAS.


In service discovery is just convenient. With SOAP libraries, using a web service is as easy as 2-3 lines of code. The library handles loading the wsdl file, generating functions with appropriate parameters, sending requests and parsing response and exception handling. Without this ability, it's a lot more code.

It's not necessarily harder, but it's more code nevertheless.


The problem with SOAP + WDSL was just that it was overly complex and, in my experience, never worked cross-platform & cross-language. And since it was so complex it was "¤%& impossible to debug at lower levels. Argh! I hate soap with such a vengeance it's actually embarrassing.


Agreed. These problems (complex tooling, poor interop, non-human readable WSDL) were my motivation for writing Barrister RPC (a JSON-RPC implementation with a human readable IDL).

For those who find value in separating interface from implementation, you might take a gander.

http://barrister.bitmechanic.com/


True. Once you have the right libraries in place working with SOAP can be surprisingly easy. I always find defining and passing data types/structures the most frustrating exercise with SOAP though, especially collections. REST makes that stuff so much easier.


The big question I almost never see addressed: Why would true REST be worth pursuing anyway? What's so good about it over other ways of setting up APIs?

If I recall correctly, Fielding himself says that it may be detrimental to an individual organization in the short term, but if we all do it, it will eventually help make our APIs collectively interoparable.


This is very interesting. The idea and requirement between ease of use and full-REST implementation is definitely worth discussing.

Two Points: * Consistency could be very useful across web-services * Simplicity is exceptionally important

If all API's were created with a 'full-REST implementation', that would comply with the need (Desire?) for consistency, however it would not necessarily be easier.

What might be "hard": * Some limitations (firewalls, lack of knowledge) on PUT and DELETE * The debate on when to use PUT and DELETE * Editing headers to request content type (xml vs json etc)

So the big question seems to be: What is the mix between consistency and ease of use?

Perhaps we can be full-on REST but then make it easier with code libraries?


Having spent a while now working on a project with a RESTish API, I can say that I'm really regretting not pushing harder for hypermedia controls earlier. It would have made pushing upgrades out a lot simpler.


Easier for you, that is. The problem with HATEOAS is that it requires commitment on both parties (server and client) to that convention. For the client developer that means no hard coding changing URLs. But they will, and you'll be to blame when your server changes break their code. You'll say "but you didn't HATEOAS" and they'll say "my app is broken, fix your API now" and you... who wants to go there?


In this case we're in control of the server and the client, so it's less of an issue, but in general it's not the ability to change URLs that I'm getting at here, and that's not a flexibility we've needed. It's the ability to add extra functionality by adding links on each resource without breaking existing clients that I'm missing at the moment.


Adding HATEOAS resource links should not break client code. It is nothing more than adding an attribute or sub-element to your xml.


Precisely.


This is an excellent and insightful overview of REST both the good and the bad. It's the Hypermedia API problems that stand out the most to me here. It's definitely the unicorn of the whole thing.


The accept header seems natural place for versions.

oh and tacks not tax http://en.wikipedia.org/wiki/Brass_Tacks


I spent decent amount of time and effort making my code generator/framework create a proper RESTful API, but I have to admit it is a bit tricky to figure out what is actually "proper." I feel like I was able to do a decent job with everything except the discovery service. If any PHP devs would like to contribute or just look at the API please feel free at phreeze.com


Interesting. He gives three examples of truly RESTful APIs, and Tim Bray was involved in creating two of them.


The web page is unreadable on my iPhone. Right side is clipped; same as another post says about Android.


Thanks for the heads up! Fixed.


Perhaps you should write a strongly-worded letter to your congressman.


I once asked Tim Berners-Lee about POST vs. PUT and he told me that POST was for insert (as in posting a nntp article) and PUT was for update. That's good enough for me.


s/tenant/tenet/g


Yep. Fixed.


This.


...is not Reddit. Stop that.


Off topic, but the right side of the paragraph is cut off on Android.


Thanks, fixed.




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: