Hacker News new | past | comments | ask | show | jobs | submit login

I agree 100% with this article. A simple RPC API spec takes minutes to define. 'Rest'ifying takes much longer, there are a million little gotchas, no real standard. Everyone has a different opinion of how it should be done. Data is spread across verbs, urls, query params, headers, and payloads. Everyone thinks everyone else doesn't 'get' REST. If you try to suggest something other than REST in the office you become the subject of a witch hunt. It really is a cargo cult of pointlessness. My co-workers have spent sooo much time trying to get swagger to generate documentation correctly as well as generate client side APIs, and there are countless gotchas we are still dealing with. It really is SOAP 2.0, when a simple JSON/RPC protocol would of done fine. Don't get me started with conflating http server errors with applications errors. And trying to do action like requests with a mindset optimized for CRUD. How much time have we wasted figuring out the 'standard' way to do just a login API call RESTfully. Please comment below how to do it, I love the endless debate of what is REST and what is not.

I agree with a lot of the things in your post, but this one in particular has produced the most grief for me:

> Don't get me started with conflating http server errors with applications errors.

I've wasted so much time dealing with 404 errors that were returned by the webserver itself (not the app) because the endpoint I was hitting was wrong or had moved, and vice-versa when I was correctly hitting the app but got a 404 error back from the API and I thought that the endpoint was wrong. And, of course, similar issues for 500 errors and the app itself dying versus the app processing normally and indicating an expected failure response via a 500 error code.

To add to all that badness, a lot of JS libraries in their async API method calls have different error handlers for success and for failure response codes, so you end up having to lump together business logic (for resources not found) and retry/error-handling logic (for the server the not working correctly) into the same failure response callback handler. It'd be much cleaner if all the business logic could be handled in a single callback and all of the failure logic could be handled in another. And, of course, you only even get to this level of badness once you figure that out; you can still waste quite a bit of time before you even realize that your callback is not being called because the JS framework is interpreting the expected 404 your API endpoint is returning for non-existent things in business logic differently than you are.

I still wouldn't go back to SOAP, but I do tend to prefer HTTPS/JSON-based APIs that don't abuse verbs, HTTP error codes, and mixes of URLs/params/headers/payloads. Better to put all of that stuff inside the JSON payload where it will only be handled by the application business logic, rather than mixing it in with all of the HTTP constructs that are used for other things as well.

Agreed I'm very much in favor of JSON body in, JSON response. The URL is just a way to hierarchically organize the endpoints. Just like in binary APIs where public methods are organized in classes and namespaces.

I've always made up my own error codes, which I embed in the 200 response since I end up having to map HTTP codes to what they mean anyway, and many libraries have their own behavior on how to handle various HTTP codes or cannot recognize anything other 200 and other (some LUA engines, for example).

The proper REST API should be specified as the set of domain-specific document formats (media-types) and have a custom browser as a client. Turns out, we already have HTML and web-browsers, so there is little point in actually building such APIs. It's always more appropriate to build a website instead. On other hand, what usually called 'REST' is nothing else but RPC where 'procedure call' = 'http method + url'. There is nothing wrong with that (with the exception of the name), but trying to satisfy any REST/HATEOAS constraints on top of RPC foundation seems difficult and pointless.

Don't agree at all. There is a huge difference between calling a function "foo()" that makes an RPC call and the relatively equivalent REST call "http.GET('/foo')". The former feels like a function call, and callers will assume it operates like one. However, in reality the former is not a function, it's making a network call, and it's incredibly unreliable.

In theory, the latter does the same thing, but it's far more explicit, the developer knows it relies on the network and accordingly that it may fail. Developers will be more inclined to plan for errors when the possibility of such errors are more obvious.

What's funny is that most consumers of REST APIs do it through a wrapper that turns it back into a statically typed RPC. REST truly is a useless middleman that no one realizes they just don't need.

I doubt that most consumers of REST APIs are doing anything statically typed. I would guess that the vast majority of REST consumers are written in browser javascript. Server side, I bet at least half are written in a dynamic language.

I don't think the parent here is talking about whether the language is static/dynamic. The point is that most REST calls are wrapped in a statically 'dispatched' function call.

So in most cases you'd do something like:

    let foo = () => http.GET('/foo');
    foo() //foo is statically dispatched in the source code here

It seems like "RPC" is being used pretty loosely in this thread. Whether foo in your example is RPC or not depends on its signature, if it attempts to synchronously return the response from the server, then it is RPC, if it just returns a Promise then it isn't.

> There is a huge difference between calling a function "foo()" that makes an RPC call and the relatively equivalent REST call "http.GET('/foo')".

Is it really a useful distinction? Let's rename our 'foo()' to 'dangerously_unreliable_with_unpredictable_latency_foo()'. Is there still a huge difference?

> accordingly that it may fail. Developers will be more inclined to plan for errors when the possibility of such errors are more obvious.

That part of your comment looks suspiciously similar to the usual argument against exception handling to me.

Dangerous and unreliable don't really capture it... how about potentialy_async_call_relying_on_network_that_raises_lots_of_exceptions_foo(), then I'd agree they are pretty similar. However http.get("foo") often says the same thing more succinctly.

And to your second point... yes, all developers should check possible exceptions, just like all children should brush their teeth. However, if you have bad habits and you aren't good all the time, then at lease brush your teeth after eating sweets, and likewise, developers should please check for exceptions around network calls.

Some time ago, another HN user commented a lack of good async support in languages and libraries caused some of the issues with early RPC. With more languages introducing futures and promises as return values or asyncronous functions, don’t you think we might finally have the tools to express that unreliability in a simple function call?

Sounds trivially avoidable.

Access the RPC call as httpSerbice.getFoo() and make it return a Future/Promise, or even a special subtype of those. 100% obvious what it does.

You're going to be calling REST methods the same exact way anyway - you have to pass around the result of that restful http.get call somehow.

How is that different than any function call that start something in the background? I have used and even written myself simple job code (with threading) that has functions like (simplified):

    int start(void (*job)(void*), void (*done)(void*), void* tag);
`job` is called on a separate thread at some point (goes through a job scheduler) and once it is done, `done` is called at the "main" thread (at some synchronization point, usually during the event loop), `tag` is just passed around for context. `start` returns zero on failure.

I don't see RPCs as anything different conceptually (after all the job might also fail). The only issue someone might have is when expecting a synchronous API, but even in non-networked code there are tons of asynchronous APIs.

The main difference is that networks are inherently unreliable. Threaded or multi-processed applications can also be unreliable but for different reasons.

The goal should be to inform the caller of the types of errors that may pop up. Obviously, with network or RPC calls, the caller should handle the case where the network is down. With threaded apps, the potential errors are more subtle, but the caller should definitely be aware that it's not a synchronous call. The function header you proposed is a bit clumsy due to the c semantics, but gets the general point across well enough.

This. REpresentional State Transfer stands in opposition to Remote Procedure Calls - except for a very narrow subset of hypertext/hypermedia applications.

The part I find most interesting about Fielding's thesis[1] is the introduction with architectural overview. He managed to map out modern Web apps perfectly - they can be REST (Web app with db/storage backend, perhaps extended with something like webdav) which is amenable to multilevel caching, smart client;movable data: json api js app, or smart client;movable code: js/Ajax - executing js delivered by server on client (subtly different from a "pure" js/json app (which is similar to an XML/xslt App).

I don't know why the hype of rest lead people to insist on conflating their architectures.

[1] http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

People treat the dissertation like it is a standard. It is nothing close to a standard. It is the source of a decade of time wasted bickering over it.

Kind of my point. It's a great dissertation, with some great ideas in it. One of them is REST (Web pages). But the other architectures are well documented in there too - with trade-offs.

Ed: not sure about "time wasted bickering over it". Bickering is always time wasted. Careful analysis of software architecture, patterns, and figuring out what you're actually trying to achieve - is time well spent.

There are fundamental trade-offs between REST and different patterns - depending on where the truth of your data recides, if you need acid or not, and where (what part of) your code executes.

Well, I'm glad you can put together an RPC api that quick, but the reason REST is so ubiquitous and why arguing against it is going to make you the subject of a witch hunt is because it's so easy to consume. Your API is useless if people don't want to use it.

But like the article mentioned, clients using REST are used to dealing with wrappers written for their language anyway. They'd prefer to not bother with URLs, query strings, and MIME types, and simply consume an API in the language that feels natural for them.

You can argue that REST is easier to debug for developers, but nothing makes XML-RPC or binary protocols inherently _less_ easier to debug. It depends on the platform and library you're using.

I wholeheartedly agree with the article. Well done.

> are used to dealing with wrappers written for their language anyway

And the wide availability of those is because it's so easy to build one over obvious REST apis.

Or in the case of SOAP, simply cannot work out how to consume it. In at least one case there was a SOAP service offered and I had a good quality SOAP client library in a popular language and I couldn't work out how to make a single working request.

Really? Was something wrong with the WSDL?

Not all APIs are public though. You could picking a communication protocol among services within a single technical organization. In that case you can decide to train everybody to use Thrift, for example, if the pros are strong enough.

I agree. My main goal building an API is to make it easy to consume.

Clients are lazy and impatient, and this is a good thing because it makes the developers work hard to make it easy to connect to their API.

Are you assuming REST is easier than RPC to develop and/or consume?

After moving to a REST based API there were endless meetings between co-workers of what is and isn't a good REST url. Our clients often come to us with dumb mistakes. Unlike RPC where the parameters go in a single place. With REST the parameters are spread across the verb, url, header, query param, etc..

I think they were suggesting that it was more difficult to design/develop (as per your example), but easier to consume if designed/developed well, and that this was the proper tradeoff.

Yes, I'm assuming REST is easier because of tools like curl, postman and even the major browsers with HTTP GET and great dev tools.

With RPC your consumers probably need to know some coding and even maybe a specific language, a framework or a library.

So yes, for me REST is easier, and I always love to see landing pages like this one - https://freegeoip.net - where the client can test the API in a few seconds by copying an example to its browser address line. This is a simple use case, but I hope you get my meaning.

I don't really understand the debate here regarding tooling. REST, SOAP and RPC are all ways to define/codify the API and the parameters. It all goes over HTTP at the end. So Curl, postman and all other HTTP-enabling tools/libraries can be used.

SOAP even has handy discovery tools that frameworks can consume and construct entire APIs in most popular languages.

It's just that no-one uses SOAP from a browser because XML is a royal PITA to write in JS. I would assume it's because all JS developers are too-busy writing more libraries-for and layers-over JSON.

It's precisely the 'simple' examples which don't address any of the complexities in the original article. read-only properties in a resource being PUT back, for example. You're not having to deal with that with simple read-only services like freegeoip.

> If you try to suggest something other than REST in the office you become the subject of a witch hunt.

It was the same way 15 years ago if you suggested anything other than SOAP. The more things change, the more they stay the same.

It's often new employees fresh out of college who are the worst. They can be obsessed with doing things 'right', which you can't blame them because they have little experience. The internet tells them REST is right, so anyone who doesn't agree with them is wrong.

POST /access-tokens

{“username”: “sam33”, “password”: “hunter42”}

Would be fine, but could include more if you were following a spec like JSON API 1.1. I really don’t have any of the problems you seem to have. But I work in a high level programming language, so maybe it’s that? Either way I find RCPs to get messy when they get bigger. Sometimes it’s the right move, but for web apps I generally prefer REST.

If you're defining an RPC protocol in just a few minutes, you're leaving a ton of stuff out. Anyone that assumes an RPC call will successfully complete or assumes the network is always there, is writing buggy code. An "RPC protocol" makes writing such buggy code easier. A REST protocol makes it slightly harder. In theory, they are almost identical. But in practice, developers equate RPC calls with function calls, which they are definitely not.

Too bad the first thing people do when consuming a REST API is to put a RPC wrapper around it (or find software that with auto-gen a wrapper for them)

Subconsciously no one wants to deal with your carefully constructed REST URLs. They just want a function name and some parameters.

Yep, this is pretty much how it always goes.

A good example to me is Stripe.

I've written over 10 applications that use Stripe and every single time, the first thing I did was use Stripe's official library for the languages I work out.

There's nothing wrong with wrapping REST APIs, as long as you wrap them in something that makes it clear they are a rest API. There's a huge difference in call foo() and calling http.get("foo").

Why does that matter? Most of the time the client doesn't care about the implementation details of the data transfer.

How is it any different in any way than any other async function? You just end up providing an unusually large number of parameters via headers and body, then at some point in the future the request completes with values and/or errors. What separates them?

Classic RPC functions would never be async, since the idea behind RPC is to replace a sync local function with a sync remote function, without having to make any changes to the calling code.

"Async RPC" is a more recent idea, but still gets referred to as "RPC", so the complaints about classic RPC still get raised since the term is overloaded.

Reading the article I wondered why we even integrate services so deeply with HTTP. The things I care about is the ability to cache at HTTP layer and the option to move endpoints. Which can be added indepentendly of the actual protocol. Moving a service to another protocol than HTTP could be an interesting option.

I think some reasons for still using HTTPS as a starting point are the ease of proxying (load balancing), the support for virtual server names, the fact it's easy to use from a web browser, the built-in encryption with TLS.

Plus, for any given large organizational customer HTTP/HTTPS are allowed through their firewall/other network security apparatus, wheras other ports require a bunch of special exceptions from the security people to use. Said people often refuse to give exceptions no matter how reasonable the request might be, so everybody ends up doing everything on port 80 or 443.

Fully agree with this (from experience)...

> A simple RPC API spec takes minutes to define. 'Rest'ifying takes much longer, there are a million little gotchas, no real standard.

The thing is, RPC is fundamentally broken due to the nature of distributed computing, and REST is not. All the time you have to spend doing things right is … the time necessary to do things right.

And REST really is very simple. The problem is the cargo-cult nature of folks who don't really understand it.

If I had a nickel for every time someone said "don't really understand REST".. often people who both think they understand it say it to each other. It's pretty funny. Guess what, we understand it fine, and we don't like it.

"cargo cult of pointlessness": that is hilarious!

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