Hacker News new | comments | show | ask | jobs | submit login
Who Cares about GET vs. POST? NoREST (swaxblog.tumblr.com)
55 points by swax 965 days ago | hide | past | web | 99 comments | favorite



Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function names are. And if you aren't thinking about the experience of real or theoretical third-party developers when building your API, why even have one at all?

If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system. It's less about adherence to some strict standard and more about helpful signaling. A GET fetches data, POST creates new records, PUT is idempotent, DELETE deletes. 4xx status codes indicate a request error, 5xx indicate a server error. That's all useful information for someone trying to integrate with your API, but you think that instead everyone should memorize your in-house conventions. Good luck getting buy-in on that.

The only partial agreement I'd give to this is that I no longer think it's wise to nest multiple levels of resource IDs (e.g. /customer/123/order/456). This is, again, out of respect for external developers. Instead, I would have /customers/123 and /orders/456 (assuming that IDs can't be duplicated between customers). A flatter URL structure (which is, note, still REST-friendly) can be an easier-to-consume experience for other devs.


Why, are external developers able to consume restful APIs without the need for any documentation on the system they're talking to? Aren't the various REST entities (and all the possible links between them, if you're doing HATEOAS) already part of a protocol the developers of external consumers need to know beforehand?


I'm speaking from the experience of a developer who has built numerous public APIs and client libraries over the years. Yes, you still need documentation for a REST API (I'm not a HATEOAS utopian), but you can easily attach your documentation to concepts with which many (probably most) developers are already familiar.

The article is basically saying "You know how lots of cars have steering wheels and pedals? Well, I think it's annoying to build cars that way, so I'm going to use a system of levers and buttons." There's a ton of utility in anchoring new systems to familiar concepts, and unless you have a good reason not to you should strive to do so.


I totally agree. That's my first argument against the idea of the OP.

In these days you have out-of-the-box so many already working API clients and already so many public API End-points that it would be really, really hard to create some ( opinionated ) better way of doing this.

Libraries like Backbone and platforms like NodeJS ( and many more ) have this idea so deep that it becomes extremely easy to create an API front-end / back-end app with just a matter of a couple of files ( even lines ). This is becoming standard in other platforms as well.

I think the debate is long time shifted from "Is REST good for my app?" to "How to best follow RESTful principles in my app?"


> Why, are external developers able to consume restful APIs without the need for any documentation on the system they're talking to?

In a fully RESTful, an external developer would be able to consume it with only documentation of the resource types used, and identification of a single base URI for a known resource from which the rest of the API's locations were discoverable. (That doesn't mean that an API owner wouldn't still want to document more, but it wouldn't be strictly necessary.)


Sure, that sounds nice in theory. In the wild, how many developers (especially those working on internal APIs) have the time & inclination to thoroughly document their entire APIs? And if it exists, many developers read the entire spec of an API before beginning development? In my experience both are rare.

Given the constraints developers often find themselves under, having an API that uses common patterns most developers already understand has great value. (Ex. 'GET /customer/123' performs a safe retrieval of a representation of the customer with ID 123, no documentation required)

This leaves both developers (API creator & API consumer) free to focus on documenting/understanding the non-trivial parts of the API rather than reinventing the wheel.


Note that this use of conventional URI structure patterns does have the value you describe, but is also completely orthogonal to REST (REST isn't about beautiful URIs -- the path part of the URI could all be UUIDs and you could follow the REST architectural style perfectly; HATEOAS, in particular, means that other than the entry point, your URIs should all be coming to you from other documents with their context in that document telling you what they are, so you don't need to parse the URI for meaning.)


Yes, there is a difference betwixt 'true REST' (as defined by Roy Fielding in his PhD dissertation) and what most people refer to as REST. I was referring to the latter: in my experience 'true REST' APIs are rare in the wild.


He didn't say external developers wouldn't need any documentation to consume a REST API. The implication made was that developers are more accustomed to REST, hence REST is easier to consume. Speaking for myself, I've found this to be true.


> If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system

That's not what the article suggests (ignoring?). I totally disagree with maintaining (http verb) semantics for operations that can (and usually do) change over time. Simple JSON posts for everything make APIs simpler. There's nothing about convention, just APIs which may have irrelevant conventions.


> Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function names are. And if you aren't thinking about the experience of real or theoretical third-party developers when building your API, why even have one at all?

They don't care what the internal function names are but they do care about the language of the domain, and if the internal program is well written then those will be one and the same. The HTTP verb model is inadequate for domains of realistic complexity; imagine a developer looking for the method that flubbleizes the worzwort. Is this POST /worzworts/12345 ? PATCH /worzworts/12345 ? If you squint at it then flubbleizing is kind of like adding another bishwiggle, so maybe it's PUT /worzworts/12345/bishwiggles ? Most people tend to realize that these domain-specific operations don't have a HTTP representation and instead make them a POST with the specific operation in the URL. But then you end up with an api that looks like:

    #flubbleize a worzwort
    POST /worzworts/12345/flubbleize
    #mrefgle a worzwort
    POST /worzworts/12345/mrefgle
    #chezzle a worzwort
    POST /worzworts/12345/chezzle
    #delete a worzwort
    DELETE /worzworts/12345
This ends up being less consistent, and more confusing to a developer, than simply using POST for every operation.

> If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system. It's less about adherence to some strict standard and more about helpful signaling. A GET fetches data, POST creates new records, PUT is idempotent, DELETE deletes. 4xx status codes indicate a request error, 5xx indicate a server error. That's all useful information for someone trying to integrate with your API, but you think that instead everyone should memorize your in-house conventions. Good luck getting buy-in on that.

The developer doesn't start by knowing that something is a PUT and trying to figure out what it's doing - they start by knowing what they want to do and trying to figure out the call. The world isn't consistent enough for them to be able to guess, and splitting the call into two parts - verb and path - makes it harder to remember once you've looked it up, not easier.

4xx vs 5xx is maybe valuable, but the very fact that you xx it suggests that the difference between 412 and 422 probably isn't important. In practice every REST API I've seen has felt the need to include a response body that a) explains the error and b) includes their own API-specific error code. In which case, why repeat yourself badly in the HTTP status code?


Maybe it would make sense if you actually looked at it like a domain.

I'm not sure of a domain where an operation cannot be represented as one of:

* Return $data

* Return a manipulation of $data that is the same type as $data

* Return a value derived from $data

* Update $data with a new value

* Create a new identifier with values that conform to the same type as $data

* Delete $data from the system

Certainly there are things like transcoding a streaming video which don't seem to map immediately, but they could if you shoehorned them. You're probably better off using something like UDP for that, though, instead of an HTTP request over TCP/IP.

Let's take images. Translation factor:

    worzworts -> image (identity)
    flubbleize -> encode to jpeg (non destructive manipulation)
    mrefgle -> encode to png (non destructive manipulation)
    chezzle -> execute image as a Piet program and return the output (derived value that is not necessarily of the same type)
Now, it's think through it a little.

    POST /image/ <http://imgur.com/8XA9Eva> => 1

    GET /image/1 -> identical response (except some http headers) to the imgur URL

    GET /image/1/piet -> "Piet"

    GET /image/1/jpeg -> /image/1 re-encoded as a jpeg
All good here. But let's look at some of the operations you suggest might be used, and the action they take:

    POST /image/1/jpeg -> re-encodes the gif as a jpeg and saves it to /image/1; does not conform to REST best practices because POST is for creating new records -- immediately the developer is confused

    GET /image/1/piet -> <unknown, I do not have a Piet runtime> because jpeg encoding is lossy, it fundamentally changes the data source at /image/1 with an arbitrary filter; in this case, JPEG compression.
Funky undefined behavior, and it may not have the side effects intended. Now let's see how it would work RESTfully:

    GET /image/1/jpeg -> returns a jpeg encoding of /image/1

    POST /image/ <response from /image/1/jpeg> -> 200 OK, { id: 2 }

    POST /image/ "Piet" -> Error 415 (images expect an image, not a string)

    GET /image/2 -> returns jpeg encoding of /image/1

    GET /image/1/piet -> "Piet"

    PUT /image/2 <response from /image/1/png> -> 200 OK

    GET /image/2/piet -> "Piet" (since png is a lossless encoding, Piet will perform the same on it)
If you conform your API to single responsibilities, you won't confuse the consumers of it by transforming a data source in-place on their request.

Things go a little sideways, but not much, if these operations need parameters. But that's why they're query parameters and not a part of the url. I don't think anyone would suggest that you create a URL like

    /image/1/jpeg/width/640/height/480
That's just silly. If you follow the single-resource-deep philosophy, though, anything after the ID is a resource that is derived from the resource residing at #ID. This URL might actually make a lot of sense:

    /image/1/jpeg/resize/640x480
But then again, so would:

    /image/1/resize/640x480/jpeg
But wait, there's an even better way to define these ... and still be restful.

    /image/1.jpeg, /image/1.png, /image/1.piet
Oops, that last one doesn't work. Piet isn't an image format, it's the result of executing the image as a Piet program. So maybe that one works better as a URL segment:

    /image/1.jpeg (image), /image/1.png (image), /image/1/piet (string)
In this case,

    /image/1.jpeg?w=640&h=480
makes just as much sense as

    /image/1.png?w=640&h=480
But since we know that instead of a transformed image, the image/:id/piet resource is a derived string based on image/:id, this URL totally doesn't make sense - and neither should it! Should the image be resized before being executed? How exactly does one change the height of an ASCII string? Maybe you want an image of the ASCII string that has been resized? Ambiguous request! Undefined behavior!

    /image/1/piet?w=640&480 -> 400, bad request.

But these same operations could be done on a video stream. POST creates a new video stream identifier; streaming source opens a websocket/webrtc/whatever based on that identifier; GET /stream/1/hls -> hls chunked stream of the incoming source video; GET /stream/1/dash -> DASH chunked stream of the incoming source video. Uh oh, source stream disconnects due to a network glitch. That's okay, they can just re-open it with the same identifier. Source stream PUT <some kind of manifest> /stream/1 : notifies the server that it is complete, server can cease to accept new streaming inputs for /stream/1; GET /stream/1/hls now returns a 301 redirect to /video/1/hls, which contains the entirety of the video that was streamed, as the server received it.

Or do you have some other domain in mind that doesn't involve flubber?


You've made everything non-destructive in your example, which I think makes REST a better fit - although even then, note how your example requests are almost all GET or POST.

> does not conform to REST best practices because POST is for creating new records -- immediately the developer is confused

You've got it backwards. The developer doesn't start with that request and try to figure out what it does. The developer starts by wanting to e.g. convert an image to gif, and trying to figure out what call will let them do that.

    POST /image/ <response from /image/1/jpeg> -> 200 OK, { id: 2 }
    PUT /image/2 <response from /image/1/png> -> 200 OK
Notice how you're offering the same operation through two different verbs here - exactly what the article is advocating!

The specific domain where I've seen REST go most wrong is contracts for large scale commercial insurance / reinsurance. Operations are things like:

* Split a contract into multiple loss layers (0-25%, 25%-50% etc.) * Reshuffle the contract hierarchy - e.g. change a ship insurance contract from being split into hull / cargo and then individually layered in them, to being split into layers and then each of those split hull / cargo * Compare two quotes, potentially made against different versions of the contract

All these are things you can encode as REST operations - you can think of particular things to be entities and declare each of these to be creating or deleting entities. But that's in the same sense that you can encode any data as binary. What we found was that users - people with actual domain knowledge - didn't think in terms of those entities. They thought in terms of these operations, which had their own names that everyone in the industry understood, and the only entity they thought of was the single "current version of the contract" - everything else was one of several different kinds of "change the contract" operation. So the only mapping that made sense to a client familiar with the domain was a bunch of different POST operations on a contract object.


> Why not simplify by removing parameters from the URL altogether?

Caching. Put a caching proxy in front of your api endpoint and enjoy.

This is the same reason you'll want to do:

    /api/customer/1234/order/abcd
instead of:

    /api?customer=1234&order=abcd
Because even if your cache includes query strings (which it may, and you can configure it to), you lose order. Now you also have to configure your cache to ignore ordering on the query string, and that's a lot less obvious (and probably not even HTTP compliant?).


I feel like the author is advocating for an easy API over a _simple_ API. Yes, the author's ideas make programming easy. There's less cruft and it's faster to develop. But, he's loosing simplicity. REST is a simple API because you don't need things like ordering, etc... That makes it compossible with other simple technologies (like the caching proxy you mentioned).


Just require query string to be sorted. In Python:

  urllib.urlencode(sorted(params.items()))


You have missed the point of REST. REST is an architectural pattern for a World Wide Web global-scale system. Your API may not fit that description. Thus, REST may not be appropriate for your API.

Following the REST patterns means you can take advantage of things like standard agnostic intermediaries for caching & filtering. You can switch or even spread across service providers by changing domain names in URLs. You can mitigate network issues through guaranteed idempotence and using conditional headers like if-none-match. These are all issues that you'll probably have to rediscover and re-solve on your own, if your API lives long enough.

It is not about SEO or visually "clean" URLs. REST says nothing about that, despite many misconceptions otherwise. In fact, in a truely RESTful service, resource URLs can be obscure garbage like http://example.com/A13D-DE45-32BC. If you're using the HATEOS principle, those URLs will be picked up from links in your responses.

"Pidgeonholing" your errors to standard HTTP error codes means that you've followed a shared standard, rather than inventing your own from scratch. Also, 4xx errors generally refer to something in the client's request, while 5xx errors generally mean something has gone wrong while generating a response on the server side.

If you don't need any of these things - because, say, you're building an API that will only be used between two parts of your own small system - then don't worry about REST. Go ahead and build something along RPC lines.

You might regret it someday, if you find yourself needing the scaling characteristics of the WWW. But, many systems never grow to that point anyway.


Pure REST is great for toy examples, where every domain entity fits neatly into a resource and every operation is a CRUD one that can be represented as an HTTP verb. So there is a million tutorials explaining how to build a REST API for a blog with BlogPost and Author resources, or an ordering system with Customer and Order resources.

However, when you move beyond these simple examples and start trying to build a REST API for a messy, complex system, you immediately run into trouble. How do you deal with non-CRUD operations? How do you deal with long running processes? Or entities in an indeterminate state? How do you aggregate multiple resources into individual request/responses for performance? How do you de-duplicate identical entities within an aggregated response? How do you return only particular fields? How do you handle paging and filtering? How do you handle transactions? How do you handle authentication and security restrictions?

All of these things can be solved, but by the time you've finished, your API will be far from easy to understand. It will need a load of accompanying documentation to make it usable, and will have a bunch of weird corners where you're initiating operations as a side effect of setting entity flags, or you've noun-ed verbs in order to turn an abstract operation into a resource. E.g. PUT /server-reboot-attempt.


Pure REST is great for toy examples, where every domain entity fits neatly into a resource and every operation is a CRUD one that can be represented as an HTTP verb. So there is a million tutorials explaining how to build a REST API for a blog with BlogPost and Author resources, or an ordering system with Customer and Order resources.

In my opinion REST is fat more easier to understand for non-CRUD resources. The whole idea of CRUD makes REST sometimes seem like an HTTP adapter to the database.

However, when you move beyond these simple examples and start trying to build a REST API for a messy, complex system, you immediately run into trouble. How do you deal with non-CRUD operations? How do you deal with long running processes?

You POST the work to be done to a resource. That resource returns a URL where you can follow the state of the long running process. An alternative would be to provide a Webhook that will be called when the long running process has finished.

Or entities in an indeterminate state?

Same principle, return its actual state on a GET request (polling). Or use a Webhook (push).

How do you aggregate multiple resources into individual request/responses for performance?

Use HAL embedding or multipart.

How do you de-duplicate identical entities within an aggregated response?

Use the hypertext caching pattern in your caching layer or proxy. Use the URL as a unique key.

How do you return only particular fields?

Use HAL embedding, query parameters or a subresource.

How do you handle paging and filtering?

Use link relations and query parameters.

How do you handle transactions?

That's exactly what POST is for.

How do you handle authentication and security restrictions?

Provide a token header over https. Avoid magic URLs based on cookie state (i.e. even when you are logged in as mike your account details still are at https://example.org/user/mike/account). Restrictions are a product of the token and the requested URL.

All of these things can be solved, but by the time you've finished, your API will be far from easy to understand.

That is true, it won't be easy to understand, but it will be simple, composable, discoverabe and scalable.

It will need a load of accompanying documentation to make it usable

In my experience the opposite is true, true REST (which includes hypermedia controls) need less accompanying documentation. It makes resources more predictable and less dependendent on URLs.

All of these things can be solved, but by the time you've finished, your API will be far from easy to understand. It will need a load of accompanying documentation to make it usable, and will have a bunch of weird corners where you're initiating operations as a side effect of setting entity flags, or you've noun-ed verbs in order to turn an abstract operation into a resource. E.g. PUT /server-reboot-attempt.

In stead of making a separate resource for every action it would suffice to make one that receives actions by POSTing to it. You can respond with a URL that represents its progress. And it will also save a lot of unnecessary links or hardcoded URLs.


Any book / link where you think we can get more details about your responses?


Most O'Reilly books about REST are really good. If you want to be more practical I suggest looking for simple ways to introduce hypermedia controls. In my opinion the HAL JSON standard is the easiest to understand: https://www.google.com/search?q=hal+json


> How do you deal with non-CRUD operations?

There is no such thing as a non-CRUD operation in a well-modeled domain. Actions in the domain are entities in the model and, therefore, are subjects of CRUD operations (often just Create and Read, but Update and Delete can also be sensible.)

> How do you deal with long running processes?

As entities that can be Created and Read.

> Or entities in an indeterminate state?

By returning a representation of the applicable wavefunction.

> How do you aggregate multiple resources into individual request/responses for performance?

By defining resources that are aggregates of other resources, and including, among the available representations for such resources, representations that include the subelements directly rather than only by reference.

This is analogous, in the RelationalDBs, to defining views (or set-valued functions, depending on the type of aggregation you are doing.)

> How do you de-duplicate identical entities within an aggregated response?

Whether or how you do this depends on the representation chosen.

> How do you return only particular fields?

By returning a representation that includes only the projection of the base resource that is of interest. I suspect the question you mean to ask, however, is how you specify a request for such a projection in REST over HTTP (REST is not tied to a particular protocol, but HTTP is the most popular one used with the architecture, and does have some particular issues in this particular case), and there are a couple of obvious alternatives:

1) Treat this as a request to create a subordinate resource to the base resource, and do it via POST, or 2) Recognize that this (like the use case met now by PATCH) is a gap in the existing HTTP method set that requires a workaround, and add a HTTP extension method (preferably, one proposed as an RFC with general semantics), such as something similar to WebDAV's REPORT or SEARCH (but not so application-specific.) REST doesn't -- with an extensible protocol like HTTP -- mean you have to be limited by the base protocol, it just means you have to be judicious in your use of extensions. [0]

> How do you handle paging and filtering?

Same answer as the previous question (in fact, paging and filtering are essentially the same thing as the previous question.)

> How do you handle transactions?

A unit of work that is logically linked in the domain is an entity in the domain that can be Created, Read, Updated, and Deleted (particularly C = BEGIN , U = COMMIT, D = ROLLBACK.) Actions within the transaction can either reference the transaction in resource representations or be subordinate resources to the transaction itself, depending on context.

> How do you handle authentication and security restrictions?

Generally, Authentication is communications-protocol dependent and how security restrictions are enforced is outside of the scope of REST.

[0] http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte... : "A REST API should not contain any changes to the communication protocols aside from filling-out or fixing the details of underspecified bits of standard protocols, such as HTTP’s PATCH method or Link header field."


What an incredibly clueless post. I thought we’d left that level of misunderstanding behind us a few years ago.

There is no such thing as a RESTful URL. Characters in URLs don’t matter.

GET and POST have different semantics. Ignoring the benefits of caching is just dumb.

The semantics of status codes and verbs are defined pretty clearly. You might have to read an actual spec.

A paragraph starting with “A core difference between REST and RESTful …” physically hurts.

I can’t believe I just wasted time actually commenting on this.


If you are mapping your RPC-style API into a REST interface, you are doing it wrong.

REST as an architectural style wants you to think as your API as a collection of resource representations that can be manipulated through simple verbs (as the one present in HTTP).

It forces you to think about idem-potency, caching, namespacing, immutability and robustness.

If your API doesn't have abstractions for long running processes or more complex business transactions, you don't have a RESTful API and need to adapt it.

Furthermore, if payload size and minimizing the number of requests is your most important goal, why are you using HTTP in the first place? It's not the right solution for you.


And object orientation wants you to think about your API as a graph of objects, but going too far down this rabbit hole is how you end up with a mess of AbstractSingletonProxyFactoryBeans[1]. Yes, it's possible to adapt any system into a collection of resources, but the transformations necessary to do so can end up creating more complexity than is gained through only having resources and verbs. The supposed simplicity of REST can be like the simplicity of the Brainfuck language. There's only 8 commands! Easy!

And the idea that using HTTP somehow renders questions of performance irrelevant is bizarre. If Hacker News sent 10MB of uncachable CSS, spread over 100 HTTP requests, with every page load, would that be OK because performance isn't the most important goal of the web? People use HTTP because it's a ubiquitous protocol supported by virtually every device and language. Performance isn't the most important goal, but it's still an important one.

[1] http://steve-yegge.blogspot.co.uk/2006/03/execution-in-kingd...


> In other languages API calls don’t return with function pointers to related functions.

Let me introduce you to a little something called objects.


Completely beside the point of the article, but just seeing a url like "/customer/33245/order/8769" makes me anxious that the author might give multiple customers the same order number.


So what?

If, in the particular domain, that makes sense, then so what? It may be that in this context, an "order" is only unique to a customer.


yikes!


Congratulations, you've discovered RPC.


Strawman


It would have been nice if he proposed something innovative moving forward, but this is a step back to RPC-structured APIs. After reading this though, I had to check to make sure it wasn't April 1st.


I think the whole point the article is trying to make is that REST isn't really a step forward from RPC. It just adds an overly restrictive taxonomy (i.e. in a complex practical application, you're probably going to run into a problem that doesn't fit the REST model well), and doesn't actually solve any real problems.


REST doesn't add "restrictions". REST describes constraints that - if you can work within them - provide certain guarantees and benefits and capabilities. It solves a bunch of real problems. REST wasn't just pulled out of thin air - it was an attempt to describe how the web itself works at global scale.


That may be the point of the article, but neither it nor any of the other instance of people making similar claims I've seen supports it convincingly.


Gaining experience in technology is absolutely exhausting. Everyday there are posts, "New Shiny X!" and then six months later "Introducing No-X". Then a year later "New Shiny Y! Like X but without sesame seeds on the buns!"


This is one of my biggest problems with the software industry. It isn't just the posts, but the tooling / utilities / frameworks / languages as well. If you are smart enough to improve something, there is no fame in contributing to the open source project, some people would rather just re-write their own and attempt to force it down everyones throat.


It's pretty much required, if you want to change what the existing project thinks is "the right way to do something"


How the url is formatted is irrelevant for REST.


Exactly, I don't understand why but it seems to me that a lot of people like to use the word REST as much as possible without knowing what it actually means, or without even having read the damn paper :(


The author CLEARLY has no idea what REST is or even why REST is used. How the hell did this garbage make it to the front page again?


The criticism they start with is weird. They take this API call:

  OrderDTO Customer::GetOrder(int customerID, int orderID)
And this HTTP call:

  GET /customer/33245/order/8769
And ask:

> If we put the function name between the parameters themselves, then it leads to the question, what part of this URL is the endpoint?

But the thing that is between the parameters is "order" not the function name "GetOrder". If you look at an alternative form in which one might (more idiomatically, in some languages) see the API call, the relation to the HTTP call is more clear:

  Customers[customer_id].Orders[order_id]  # => Order
> REST URLs look ‘cleaner’, but are they easier to understand?

Yes.

They go on to say:

> A simpler API, similar to our original function call would have no mixing of parameters with the name, and clear definitions of the parameters being passed in.

With this example:

  GET /customer/getOrder?customerID=33245&orderID=8769
How is that simpler? Sure, depending on how you've implement the backend, it may be a more direct reflection of the backend code, but there is a difference between "leaky abstraction" and "simpler". Also, why is the fact that the action is "getting" something reflected twice?

> Why not simplify by removing parameters from the URL altogether? Put them in the query string, or post body as JSON.

Sure, RPC-over-POST is an alternative (older than REST) style of Web API, and if that's what floats your boat, do it. OTOH, "removing information from the URL for aesthetic reasons" may not be the best reason to move a safe operation from GET, which is defined as safe, to POST, which is neither safe nor idempotent, particularly if you are using middleware that is aware of HTTP semantics -- you've just thrown away valuable information.

> Your API can encounter many application specific exceptions while processing a request, but why try to pigeon hole your error response into a limited number of error codes designed for retrieving hypertext?

Why not choose the best error code of those available in the protocol? In a traditional programming language, would you just throw the most generic possible exception? Because that's what the "just use 200 for success and 500 for failure" approach is equivalent to.

> Verb agnostic, HTTP verbs are really only needed to identify where parameters will go – for GET it’s in the query string, For POST, in the body.

If you are going to use HTTP, why fight against it by throwing out its defined semantics? Aside from reducing the value you get from existing HTTP-semantics-aware software, you are just forcing yourself to reinvent the wheel.

> REST is a style, not a standard.

Whether REST is a style or a standard is irrelevant when your post is advocating abandoning it completely in favor of RPC-over-HTTP with deliberate disregard for HTTP's defined semantics.


> advocating abandoning it completely in favor of RPC-over-HTTP

It surprises me that the author didn't go one step further and ask us to return to SOAP :)


I think that we're only a few years away from someone reinventing SOAP but with JSON playing the role of XML.


And a hip name, like Dove or Ivry.


I hope all the backends are written in JS and run in the browser.


I take it that no one's seen JSON-RPC? http://json-rpc.org/


Other than XML Schema, and XML namespaces, what was wrong with SOAP? Serious question. I genuinely think it had big advantages over REST.


I would say the main PITA with SOAP is the fact that all the methods exposed by a service are on the same http endpoint. So without capturing headers you don't even know what operations are being called. Take Apache logs for example - all you'll see is a load of POST calls to /MySOAPServiceEndpoint. Whereas with a nicely designed REST service, you can at least see the gist of what is happening without additional logging.

And take testing - you need to pull out the heavy artillery (SOAP UI is very good) to test a SOAP service. Whereas REST can be done quite easily (even more so if it's GET), from a browser or a simple python script.

Not huge issues, and if you're working on a big project it won't make a big difference. But still a PITA, in my opinion


> Take Apache logs for example - all you'll see is a load of POST calls to /MySOAPServiceEndpoint. Whereas with a nicely designed REST service, you can at least see the gist of what is happening without additional logging.

Fair enough. I always preferred to log at the "middleware" level but I guess that's a use case.

> Whereas REST can be done quite easily (even more so if it's GET), from a browser or a simple python script.

GET for everything is a large part of what the article is advocating. Once you get into "true REST" (using PATCH/DELETE/etc., maybe content negotiation) then you quickly move out of what you can test with the browser.

As for Python that was one of the things I loved about SOAP - plug the .wsdl into suds and you can test it from your Python script and you don't even need a separate doc, you can just use Python's built-in dir() and help().


Yes I definitely agree with your last point - one of the nice things with SOAP is the autogeneration side. Same thing with Java CXF - import your wsdl and boom you have an interface with all the right methods etc, whether on client or server side.


The "point" of SOAP is to have an autogenerated API layer + objects on client and server side.

The problem is that this does not actually help much in validating data sent or received. In addition, the client side SoapFooData you might receive is unlikely to be something that you want to pass around, so you still need wrappers for most complex structures.

Working with SOAP is complex even for extremely simple APIs, and for complex ones you will end up NOT using the generated objects anyway.

Finally implementations are generally buggy and incompatible with each other. And generated code is something that should be strongly avoided.

So in short: SOAP is complex and in return you get something that's pretty much useless (generated objects)


> Whether REST is a style or a standard is irrelevant

Yes it is relevant.People right on HN spend their time arguing over what is restful and what isn't ,because vague "semantics". with RPC,no argument.And to be frank, if you're not using HATEOAS , there is very little difference between a api with beautiful urls and a few headers and rpc.

The original spec is brilliant yet way to vague to be useful. RPC isn't HTTP,which is totally handy in the context of micro-services as one can implement RPC over any protocol. that's not the case for Rest...


> People right on HN spend their time arguing over what is restful and what isn't ,because vague "semantics".

The defined semantics of HTTP/1.1 methods (REST is a style, but HTTP/1.1 is a standard) are not vague.

> And to be frank, if you're not using HATEOAS , there is very little difference between a api with beautiful urls and a few headers and rpc.

If you aren't using HATEOAS, you aren't using REST. That doesn't necessarily mean that it is indistinguishable from RPC.

> The original spec is brilliant yet way to vague to be useful.

What original spec are you talking about?

> RPC isn't HTTP,

Neither is REST, though HTTP is itself an example of REST and a protocol which is fairly convenient for use in REST services.

> which is totally handy in the context of micro-services as one can implement RPC over any protocol. that's not the case for Rest...

> On the contrary, it is rather central to the point of the REST architectural style that you can implement it over any protocol (or, given hypertext formats which support identification of different protocols, over any combination of protocols simultaneously.)

From the creator of REST [0]:

A REST API should not be dependent on any single communication protocol, though its successful mapping to a given protocol may be dependent on the availability of metadata, choice of methods, etc. In general, any protocol element that uses a URI for identification must allow any URI scheme to be used for the sake of that identification. [Failure here implies that identification is not separated from interaction.]

[0] http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...


> If you aren't using HATEOAS, you aren't using REST.

Then almost no REST api i've seen out there the is a REST api,which is my point exactly.

If you're writing a spec almost nobody understands and almost nobody truly implements yet everybody think they implement, then you failed at writing that spec. Because I can guarantee you out there a only few people understand HATEOAS.

And that's the heart of the problem. The reason is easy. While the idea of using a few verbs, etags and content negotiation is easy, HATEOAS is hard,period.It's hard the person who implements the api ( because someone has to code all the logic representing these states, the links, ...) ,it's hard for the client: the client has to be smart, urls aren't hard coded but need to be fetched from somewhere , and the body of a response needs to be parsed to know in which state the resource is, the actions, the relationships between resources, or sometimes the links are in the headers... You're not just sending data but also metadatas (just like RPC) that are only relevant in the REST logic, not in the actual application logic ... you're basically writing a web bot every time you're writing a rest client... All that for what? elegance basically.

As your link says, most devs are just using RPC with beautiful urls to begin with and stamp "RESTfull" on that. Don't you think there is a real issue here? I do.


Everyone writing a web page with links you can click on to navigate through a web site understands HATEOS.

It's just that, for some reason, when you shift to imagining a robot interacting with your site rather than a person, people get confused.

Though, really, I know that the reason is that they're trying to shoehorn an existing set of APIs into this cool REST thing they've heard about. In reality, you need to look at it from the other direction. Or, don't try wrestling with REST.


OTOH, "removing information from the URL for aesthetic reasons" may not be the best reason to move a safe operation from GET, which is defined as safe, to POST, which is neither safe nor idempotent, particularly if you are using middleware that is aware of HTTP semantics -- you've just thrown away valuable information.

So a part of the point of and value of GET is to communicate idempotency? We can know certain parts of the API are idempotent because they are GET?


PUT and DELETE are idempotent, GET is safe (and idempotent, but safety necessarily includes idempotency, since "no side effects on resource state" also means that duplicate requests have no effects on resource state beyond that of a single request.)

But, yes, a key thing communicated by HTTP method used in a call is whether the call can be relied on to be safe, idempotent, or neither.


> So a part of the point of and value of GET is to communicate idempotency? We can know certain parts of the API are idempotent because they are GET?

Yes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html


Pedantically, RFC 2616 is obsolete, and you should point to http://tools.ietf.org/html/rfc7231#section-4.1


The proxy will rely on distinction between GET and POST to know whether to cache.

More importantly, your enterprise Load Balancer with timeout and failover configured will feel free to repeat a GET request internally to a different server when the first server takes too long to respond. For a POST, it will instead throw an error to a user and perhaps trigger internal alert.

And if you think that's not a big deal, wait until your bank does a double international money transfer because their trigger call was implemented accidentally as a GET request and the network latency was bad. And hope, just hope, that they are not running 10 different servers behind the firewall and therefore falling over 10-1 times.... (true horror story from my tech support days).


I don't understand why it isn't

GET /getOrder?customerID=33245&orderID=8769

I mean, if you're going to go with this style, might as well go all-in on it, right?


You surely meant:

GET /index.php?do=getOrder&customerID=33245&orderID=8769


Microsoft-stack developer doesn't understand how HTTP works. Film at eleven.


I've been feeding my family and paying my mortgage using the Microsoft stack. God bless it. I also have never understood the REST mojo. I agree with the original poster. For my money, a url that resolves to a public API end point, with query string parameters to encode the arguments, is the most straightforward way to do RPC over HTTP I've used myself. When things get more complicated, as with an ajax client, then embedding JSON in the request body has also always worked well for me. Would straight up REST be better? Maybe, but I don't see it.


Hey, I didn't say anything against the Microsoft stack; I've made a living off it myself, at times, although mostly at a lower (i.e. VBA) level. But it doesn't really lend itself to easy understanding of REST.

As far as REST vs. RPC goes, there is a sole essential difference: the semantics of interaction with a REST resource are defined in RFC 2616, and a correctly designed and implemented REST resource behaves in accord with what you find there.

REST itself, meanwhile, being just HTTP, is very simple, and very well suited to expressing interactions with resources and collections of resources. Especially in the front-end/full-stack development world, most things can be expressed, with little or no semantic strain, as resources and collections of resources, and capable HTTP clients are highly available. (And the convention of encoding structured data as JSON in request bodies is very strong, besides.)

Conversely, there are as many RPC-over-HTTP standards as there are implementations; since doing RPC over HTTP necessarily means mapping whatever semantics you have in hand to an interface not designed to express them well, this makes headaches for people who have to develop against them, especially when writing an application that draws together many disparate APIs in order to do something interesting at their conjunction.

Whether REST is better than RPC over HTTP, or vice versa, strikes me as a question for a philosopher. But, in most cases of interacting with a remote resource over an HTTP transport, REST is certainly more natural, that is, closer to the native semantics of the transport, which results in thinner glue layers and more easily understood behavior.


Wait, was this a serious post or some sort of The Onion-like satire?


REST is good, better than RPC, SOAP... finally people can understand and use APIs easily and happily. The URL /customer/33245/order/8769 is perfect, looks like a directory path and reflect the relationship between the entities. Arguing about the best url, status code and best verb is the irrelevant part in REST. Why can't people just do whatever it works for the users while respecting the standards when they can ?!


I think you've inadvertently put your figure on the part that really matters. REST is worse than SOAP or something like Thrift for discoverability, worse for ease of use, worse for practically every criterion you could think about. The one thing it does right is being easy to explore with a web browser.

So optimize for that. Make it easy for a developer to look at your API with a web browser. This means simple URLs. It may mean a limited form of HATEOAS, in terms of providing links to the next things.

But it absolutely doesn't mean using HTTP status codes to convey important information. It doesn't mean using content negotiation for API versioning. It doesn't mean adopting every new HTTP verb, and frankly there's a lot of value in making every endpoint accessible over GET and POST. Those who insist on strict REST are missing why it succeeded.


For discoverability , it's a feature not a bug, when i want to use a REST API I can read the docs written for humans not try to find my way in a WSDL. Also a will built REST API is straightforward once you know the entities that you will be manipulating. IMHO the only bad thing about REST is; it's natural for CRUD, but not as easy for non-CRUD apps.


If you use the wsdl for discovery, then you are doing something that's rather dangerous as there are no guarantees what that method does or that it is correct -> use documentation instead. If the call is something important (e.g. money transfer) "exploring" rather than knowing is grounds for firing someone.


> REST is worse than SOAP or something like Thrift for discoverability

How?

> worse for ease of use

How?

> worse for practically every criterion you could think about.

I can think of lots of criteria, but very few on which I can see a compelling argument for REST being inferior to SOAP except "degree of support from libraries written to support SOAP".


There's no WSDL or .thrift -equivalent for discovery, and no autogenerated client code which makes use harder. Getting up and running is slower because you have no way to validate that your messages are well-formed short of throwing them at the server and seeing what it sends back. What criteria are you seeing REST as better on?


HATEOAS provides both discovery and, when well-known content types are used, potentially much richer ready-to-use client code than the autogenerated host-language interfaces provided by SOAP and similar protocols.


HATEOAS only lets you discover something by going there, so to speak; it doesn't give you a complete index like a WSDL.

At the transport level you get much richer and more structured client code from SOAP. Automated HATEOAS clients are at this point purely theoretical, and anything you could put a content-type on and send over HATEOAS you could equally embed in a SOAP response. If the thing you wanted to embed can be expressed as XML, you can do much better.


Your browser is an "automated HATEOAS client". You can toss it any old HTML doc and it knows what to do with things described in the HTML content-type spec such as links, forms, images, scripts, etc.


No, the browser is very much a manual HATEOAS client.


Incorrect. When you view a html page, the browser fetches the javascript, css and images in the page automatically.


> HATEOAS only lets you discover something by going there, so to speak; it doesn't give you a complete index like a WSDL.

HATEOAS doesn't inherently require that you "go" anywhere to get a hypertext document describing an API, and can be as complete as you want. But, since its really designed for web-scale decentralized interlinked APIs, no, its not primarily intended to have one document that gives you every possible linked endpoint.

> Automated HATEOAS clients are at this point purely theoretical

Googlebot is an example of an automated HATEOAS client that handles a wide array of content types. It is decidedly not purely theoretical.


Googlebot is not an automated HATEOAS client, it's solely an automated web client. Even if the web conformed to all the specifications it would still be an exceedingly atypical example of a HATEOAS API. And googlebot is nowhere near the reliability level that we would require for most use cases.


> Even if the web conformed to all the specifications it would still be an exceedingly atypical example of a HATEOAS API.

The HTTP-powered Web is the prototypical REST (including the HATEOAS constraint that is part of the definintion of REST) API.


Then REST is a wrong word, one that doesn't point to a natural empirical cluster. The web is very different from all the other things that are called "REST API"s.


Most, but not all, "REST" APIs (other than the web) violate at least one element of the definition of REST, most commonly they don't actually implement HATEOAS, requiring out of band knowledge of applicable resource URIs or URI patterns and/or information about data formats not communicated by documentation of media types and hypertext relations.

But that's because REST is often used as a buzzword by people who don't know what the architectural style entails, or aspirationally to describe one goal of the design of an API, but one that may be compromised for conflicting goals.


It's not 'discovery' in the sense that you can't discover an url, change an id here and there and build an url for another product. You don't get endpoints for operations, you get operations that are valid only for that particular product and only for that particular state.


I am increasingly getting irritated with REST for our app. It takes too much thought to figure out the "correct RESTful approach" when building out an API endpoint. And then again, there are no right answers. And all the JSON serialization/de-serialization logic, for every single endpoint. Too. Much. Work.

I'm wondering why Thrift/Proto-buffers aren't popular? Even for browser-based JS clients. What're the pitfalls?


Most of the things you describe have nothing to do with REST. That might be part of your problem :)


Is it just me or GET /customer/33245/order/8769 is wrong You have unique order id... then use it GET /order/8769 like that ??


What if orderID is NOT unique across the system?


How about this then?

GET /index.php?do=getOrder&customerID=33245&orderID=8769


"Does any language force you to prefix a function with 4 verbs?"

Actually, I don't really know. But for sure HTTP isn't forcing you to only use GET/POST/PUT/DELETE, even if this verbs are usually enough to design your API. It's quite funny to hear some people (I'm not talking about the author) joking about the "418 I'm a teapot" response status, which is defined in a HTTP extension, and then telling you that having only 4 methods to design an API is too restrictive.

HTTP is extensible, so why misusing it?


Well REST allows for resources to be accessed as they are: resources. Using POST/GET query parameters implies that the server should do some actual processing on these to deliver the requested resource. With REST, you can have the resource be a static file on the server, no processing required.


I don't think you need REST to access a static file.


Sure, but how convenient it is to just generate the resource once and for all and dump it in the file system following REST semantics, thus making it useless to waste CPU cycles computing the resource contents when it's just there.


> I don't think you need REST to access a static file.

HTTP is a -- the prototypical -- RESTful system, and its not exactly uncommon for accessing static files on the web.

REST isn't just special-purpose APIs built on top of HTTP (nor, for that matter, does it require that an API be built on top of HTTP at all.)


Static html files are the purest form of REST. What says HATEOS more that <a href=...>?


Thank you! I was wondering why I couldn't just use post for a function I'm writing right now. It helps to have someone else say out loud what I was thinking.


Just another kid with a keyboard and High Speed Internet.


The answer basically is caches and proxies.


Why you use GET vs POST are conventions. There are no way to enforce any rules in web services and conventions help to make the things you are looking at recognizable. And conventions do change over time. I think we need more HTTP verbs like START and STOP and I wish POST was NEW. Merge vs. update-all for PUT is still not well established and now we have PATCH. Such terrible names. I would love something like REPLACE and MERGE or something more descriptive.

I also think that your customer order example could have been done better. Something like

Your example: OrderDTO Customer::GetOrder(int customerID, int orderID) But that's the wrong side of the equation, that's the definition of the function. If you call the function it would look like this: GetOrder(myCustomerID, myOrderID)

And an equivalent REST call would look like this with a little clean up. GET /orders/?id={id}&customer-id={customer-id}

Doesn't look that much different to me. Sure you could have done GET /orders/{id}/?customer-id={customer-id}, but there is no reason why the above is wrong from a RESTful perspective. Why would you marshall a whole JSON payload to do a simple GET? You are not creating a tuple to pass the two parameters in your full-language function example, what's the reason it is required for the RESTful call?

As I mentioned above HTTP needs a couple more verbs and aliases for the existing verbs. You think of situations like:

START /timers STOP /trains

A lot of the time we jam POST and PUT in this situation and they are poor substitutes for a larger verb vocabulary. I have built a few automation projects and any actions you do can usually fit into

NEW GET UPDATE (merge) DELETE START STOP OPEN CLOSE

still limited, but pretty good at covering almost all the verbs needed. You supply the nouns.

NEW /vms START /vms OPEN /remote-desktop/1234 CLOSE /files/folder/path/to/file

Especially with persistent connections in HTTP2 and web sockets and things of the kind OPEN and CLOSE will likely be need to establish these kinds of connections. You could probably get away with using START and STOP though, such as:

START /remote-desktop/?computer=1234

As for HTTP error codes. They need an overhaul. 404 and 403 do the job fine, but giving the user an idea of whether their JSON was malformed (Syntax) vs. the data in the payload was invalid is difficult to distinguish with just 400. But the return payload should describe the error in detail. Not many conventions there to help.

As for HATEOS, I used to think it was great, but I am meh on it now. It needs refinement.




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

Search: