63 comments

[ 3.1 ms ] story [ 116 ms ] thread
I continue to hold the crank opinion that http verbs are a useless spandrel.

How much of a verb doing what the verb suggests it does is up to the developer? The answer is 100%. Verbs provide no guarantees whatsoever, they're just an invisible thing that isn't in the URL.

Why is something like that an invisible thing that isn't in the URL, with no specified behavior?

Because it shouldn't exist is why.

Http status codes barely do anything to begin with nowadays. I was bombarded with them in college, and if you look at any webapp, it only ever responds 200, 302 or 500.

Redirects are done in js, 404s are just normal pages, if that, and 401s are just not a thing on the public web anymore.

I regularly see 401s and 403s on public APIs when interacting with them.

301s can be somewhat handy in certain circumstances too.

I definitely use a bunch more than just those three, maybe as many as a dozen at various points. Probably another dozen occasionally leak through as problems from infrastructure we use. I could probably name several codes that I can't see much point in, but I'm sure people would respond why I'm wrong.
> Redirects are done in js

Redirects definitely happen in more places than you think/see, mostly because almost all tooling handle redirects transparently to the user, so you don't really notice it.

Doing redirects in JS is a huge waste of resources as well, as you'll need to load a full page with it's body (meaning the browser will parse and execute it) while having a 301 + Location header means the browser can redirect quicker without executing more stuff.

Not quite useless. POST tells the server to check for a body which I think is a reasonable indication of intent. But otherwise, yes, it’s all arbitrary and just vague hints to the server program as to what you might possibly be thinking of doing…
But then GET as well as everything else can have a body, so most servers would check anyway.
GET isn't really supposed to have a body:

"An HTTP GET request includes request header fields and no payload body and is therefore transmitted as a single HEADERS frame, followed by zero or more CONTINUATION frames containing the serialized block of request header fields."

https://www.rfc-editor.org/rfc/rfc7540#section-8.1.3 (HTTP/2.0)

"A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request."

https://www.rfc-editor.org/rfc/rfc7231#section-4.3.1 (HTTP/1.1)

I've never used one or spec'd one, I consider it unsafe.

This is, of course, what the specs say, and they don't even really agree; I am well aware of the non-trivial daylight between "what the specs say" and "what happens in the real world". I point this out not as a spec compliance maniac but as something people ought to know... it's not a great plan to expect GETs to be able to have bodies, just as I would not suggest sending a 404 response code that also has the "real data" in it for whatever reason. You're just asking for trouble, even if it might sometimes work.

> just as I would not suggest sending a 404 response code that also has the "real data" in it for whatever reason.

Do you mean to suggest that you advocate for an everything-is-200-OK design and the client should be checking for an error object there?

"Don't do something really stupid."

"Do you mean to suggest that people should do this other stupid thing then?"

Come on.

There's no reason for you to hold that opinion so strongly.

There's nothing inherently wrong with having a 200 for everything with a standard body structure, and there's nothing wrong with having a GET request with a body.

Where things go off the rails is strictly adhering to a specific thing without understanding the advantages and disadvantages of other approaches.

RFC 7231 has been obsoleted by RFC 9110 since June; and interestingly enough it says absolutely nothing explicit about whether body in a GET request is allowed or not.
The point is that they're ALL unsafe.

The angst around GET verbs is that someone could really be doing an update if it has a body. Well guess what, someone can be doing an update based purely off the query string too.

This angst around GET bodies is rules lawering AT BEST. It's the type of distinction that has no real merit and I consider it an absolutely short-sighted decision by the OpenAPI Spec to insist that GET has no body (they've recently changed this, but I can't believe it was ever a thing. Finally some common sense).

---

TO BE VERY CLEAR:

Back in the day you would hear stories of companies getting entire sets of data wiped out from a search engine bot that was happily following GET requests that would delete data.

There is absolutely nothing in a GET request that guarantees anything except what the developer decides to guarantee. That a developer SHOULD choose that a GET request be idempotent is still a choice regardless of the existence of a body or not.

I agree with you for the most part. Like I said, I consider it too dangerous to use, not that it's somehow impossible. But the reason I consider it too dangerous isn't that it's a Violation of the Holy Spec... everything and everyone violates the spec, to a first approximation. The problem is that all the libraries in the world and middleboxes and everything else violate the spec in different ways, and allow/enable/afford their own set of violations that do not match.

If you spec an HTTP API as having a GET with a body ("because otherwise the query string will be too long" or something), I can virtually guarantee you that as soon as it is even slightly popular you're going to get "But I'm using this library over here in some language you've barely heard of and its author thinks GET can't have a body so it won't send it to you, plz hlp".

I wouldn't consider it safe to deliberately use in an API. Similarly, I once accidentally sent a real reply as a 404 response (just a plain ol' bug), and while I did of course fix it quickly in my API, the user on the other end didn't have a good option for just ignoring it and moving on in the meantime. Their HTTP library got really feisty about using the body of a 404 ... I think it was something like, you could read it as a bag of bytes but all the clever "let's automatically JSONify this" and stuff functionality in their framework wouldn't fire on a 404. This is why I mentioned that one. It is a bad idea to treat the content of a 404 response as anything other than an advisory response to a human, and even that has some funny characteristics based on browser screwiness over the years.

There's definitely violations of the spec safer to specify, and violations less safe to specify; they're not all created equal.

That's a fair criticism. On the other hand, I do find it useful to be able to construct an API where `PUT /object/1` will create an object, and `DELETE /object/1` will destroy it. It helps me keep things a bit more organized that way, but it wouldn't be hard to add the verb to the end of the URL instead.

I would probably be in favor of some more strict guards around specific verbs, but I don't really know how that would be implemented given that the behavior is always controlled by the developer at the end of the day.

Web frameworks adhering to these conventions is probably a good thing, though, and in my somewhat limited experience, that seems to generally be the case (though they often still let the developer override the behavior).

Browsers behave differently based on the HTTP method, though. For example, POST requests are confirmed when trying to refresh, they are not cached, and a whole bunch of restrictions on XHR/fetch.
It's my understanding that a lot of early design decision like HTTP verbs happened due to the much wider spread of proxies.
It's not about the "end user" developer of the server, it's about the middleware and the clients. A browser (for example) will happily retry a GET request that times out, but will not automatically retry a POST request that does the same. Will that break a badly behaved web API? Yes. But that's like saying that "calling conventions are a useless spandrel because the consuming code can ignore the convention and just read from arbitrary memory".
That's actually a good example, because if the standard specified that POST must be idempotent, and that a POST without a uuid is invalid, we might have something here.

Instead there's a workaround for a serious problem, and any client (browser being by no means the only client) which doesn't respect the convention can emit identical POSTs, with no way of knowing if that was intended.

The standard says "POST means NOT-IDEMPOTENT" and "clients MUST not re-issue a non-idempotent requests" and the client behavior naturally falls out of those two constraints.
The standard being completely backward and impossible to enforce is a problem!
standards are agreements, they've never been there with the expectation of enforcement.
Unenforceable and unenforced are very different things.
Unless you've got state actors behind it, it's unenforceable, full stop.

We saw this when MS would regularly ignore HTML standards.

POST is intentionally not idempotent. If you want impotence, that's what PUT and DELETE are for. Sadly, browser support for those two methods is somewhat lacking. You can't specify that a form send a PUT request, for example, only GET or POST.
I agree.

I can understand the annoyance for there being so many verbs, but at the very least, there must be two: One that is guaranteed to be idempotent, and one that isn't.

Someone I know was trying to make a message board that was compatible with every browser. EVERY browser. Including Mosaic and Netscape 1.0. Which meant that everything was done using GET requests, including posting messages.

He changed his tune when some spidering engine found his site and issued tons of GET requests that ended up flooding the board with comments and posts that looked like web search queries.

I find that hilarious because that used to be such a well understood phenomenon that I can't believe no one told him about it.

OTOH, sometimes the best teacher is experience.

But there are many guarantees browsers impose. The verb is incredibly important.
Well by that notion, all command-line executables should be stripped of their names and referred to by UUID, and any text-to-UUID mapping be the sole responsibility of the user.
I can only assume this whole post is meant as a joke. HCF obviously. HELP and BUY are throwbacks to when protocols were designed with interactivity in mind, and are useless today. UNDO and REMEMBER are also without merit, the former implying statefulness on the server's part (if the client is sending the state to undo to, it's just PUT), and the latter being impossible to implement (could not distinguish browsers behind NAT, or running on same box, etc).
UNDO makes sense if the server is keeping a bag of responses. For example, ActivityPub has an Undo action that points at a previous action punished by the same user. You use it to unfollow, unvote, unfavorite, etc. Obviously, handling of Undo has to be at least partially specific to the thing being undone, but they are all expressed using the same primitive.
> UNDO makes sense if the server is keeping a bag of responses.

I disagree. Any UNDO action is just a DO action with a spcific set of commands executed in a specific order. If you use POST and PATCH and DELETE to apply actions, not only can you replay them but you can also trivially support a POST /resource/<resourceId>/undo. This is particularly relevant in systems that employ an event sourcing of some sort.

Yeah, it's gotta be a joke. The only one that actually sounds useful to me is HELP.

I have to disagree with you on that one, a HELP that could explain the endpoint and verbs? that would be AWESOME. kind of like `kubectl explain`, which has been helpful to me on multiple occasions. Of course 99% of apps would not implement that, but frameworks could easily give you that for free as the dev, and reuse the declarative code that already exists in the router... There are already tools that generate swagger files and such, seems like it wouldn't be much harder to render a server response/document on demand. You could even serve a beautiful doc page viewable in a browser! The more I think on this, the more I like it.

That's pretty much just entrypoint for HATEOAS, isn't it?
FWIW, this is a huge benefit of GraphQL. All GraphQL servers that publish their schemas can instantly have their schemas loaded with full documentation and an interactive execution environment with GraphQL Playground.
Is it really a distinct benefit though since something similar to that have been existing for a really long time before GraphQL already? From OpenAPI/Swagger all the way back to HATEOS/HAL and RDF, if not even earlier.
It is a distinct benefit when your query playground is schema-aware and can suggest completions for types, fields and field (query) parameters, as well as validate everything pre-request.
I've worked with all of those options before I started working with GraphQL. The "out-of-the-box" support, especially the standardization that GraphQL enforces with its schema definition, is/was night-and-day compared to something like Swagger.
I'm not generally a fan of graphql, but yes it's a remarkable feature. The other options (like swagger) take more effort and tend to not be maintained well. But even when they are, it's still not as developer friendly (to the client) as graphql.
Isn't there already the OPTIONS verb which does the same thing as a possible HELP method?

>The HTTP OPTIONS method requests permitted communication options for a given URL or server. A client can specify a URL with this method, or an asterisk (*) to refer to the entire server.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OP...

Yes, good point, OPTIONS would probably cover this use case. Anybody know of a framework (such as rails, phoenix, laravel, etc) that offers that? would be super interesting
> The only one that actually sounds useful to me is HELP.

And we already have OPTIONS for that.

> Remember that scene in every Star Trek movie where the captain shouts "Computer! Initiate self-destruct sequence. Confirmation code Heliotrope Banana Daguerreotype." Let's extend that so that clients can request that a (virtual?) machine shuts down.

That password is much too complex. I'm familiar mainly with Captain Kirk's auto-destruct password, which was "000destruct0".[0]

[0]https://m.youtube.com/watch?v=IlMFI8Zz7C0&t=54

also used in Picard S2 Ep1
Isn't the code the vocal fingerprint of Captain Kirk? That also explains the delay between the Kirk giving the code and it displaying on the screen.
This is an amusing what-if discussion but none of these semi-serious suggestions would be workable.

The beauty of HTTP is that servers are simple (HTTP2/3 complicates things). HTTP clients have to implement all sorts of painful encodings and caching but a server (begin in charge of the conversation) can ignore all of that and be incredibly dumb.

Of these suggestions, BUY might be the most useful but it is really just a PUSH with extra data that needs to be sent anyway.

REMEMBER (server side cookies) sounds like a good idea (after all, HTTPS, etc maintain state per connection) but quickly becomes a DoS attack once a client decides to get the server to REMEMBER 10 million sessions.

HELP is not useful, nobody uses HTTP interactively and clients don't really bother negotiating any more.

God knows how UNDO is supposed to be implemented.

> The beauty of HTTP is that servers are simple (HTTP2/3 complicates things). HTTP clients have to implement all sorts of painful encodings and caching but a server (begin in charge of the conversation) can ignore all of that and be incredibly dumb.

That kinda stopped being true with all of the security theatre headers browsers want app to provide to be "secure". First we had CORS, that failed and had to be expanded every year or two, now we have the whole capabilities things, which means web server can't just serve web and even simple server serving static JS with maybe some JSON API needs to have logic for generating right headers for right page.

I’m not sure CORS has failed. It’s forced a lot of teams to put internal apis on the same host, which is a security win.
Yeah, either putting the content + backend behind the same Origin, or just add `Access-Control-Allow-Origin: *` as a header to all requests (and the other ones), which one you think is most popular for people with no time to build things properly? ;)
I agree with you, but there are those of us who push for putting everything behind the same origin and we succeed in that because most people are aware of how painful CORS is.

So, in some ways, CORS being so painful is actually helping (tongue-in-cheek, but it has some truth to it).

With CORS we can choose from:

1. Apply more specific security appropriate for your use case

2. Put it on the same host

3. Origin *. Teams that do this probably have bigger security problems anyway.

Without CORS, we'd all be stuck choosing between 2 and 3.

The server doesn't really need to serve those headers - what does it care if the client won't load the scripts, or whatever? HTTP is more than serving HTML.
I am amazed that after so many years of smart contracts existing, something like an API micropayment standard doesn't exist yet.

The root of the API service, example.com/api/ could GET a "service descriptor file", which describes all endpoints of the API, and the crypto address of the API provider. The first time you use such an API using a client which would handle all the payment stuff in the background, it would open a payment channel by depositing some amount in a smart contract with a standardized interface, and each subsequent HTTP GET to the chosen endpoint(s) would be supplied with a payment channel signature in its header (something like "X-Signature"), incrementally signing off on more transfers offchain.

The key to this would be to

(1) create a well-accepted payment channel contract

(2) create server and client side middleware that handles all this automatically

On the server side, something like https://www.hug.rest would be an ideal starting point, it automatically generates documentation in JSON format at the API root

I'm absolutely not, because that is not in interest of any of the payment provider to make it easier for the user to change for competing, cheaper payment provider

>The first time you use such an API using a client which would handle all the payment stuff in the background, it would open a payment channel by depositing some amount in a smart contract with a standardized interface, a

and crypto is terrible way to deal with normal money transactions on every level

Disagreed, in some instances this could be the easiest way of getting an API monetized. No messing around with a third party service. Clients wouldn't need to register anywhere. Considering the many new machine learning APIs, now might be a good time for this.

Crypto does have its problems, irreversible transactions can be a big problem. But with microtransactions, it might not matter that much.

Similar things have been proposed. Quite apart from crypto being useless for this, the problem with micropayments is that they have all the problems with traditional payments (fraud, chargebacks, money laundering) without the benefit of being able to make much money of transaction fees. So nobody wants to provide the service and I don't blame them.
> nobody wants to provide the service

I could imagine many more machine learning APIs popping up if it is created. Monetizing an API currently requires various third parties

> ~nobody wants to create something like this

Yes, because it would be a common good, for which there is no financial incentive for its potential creator - even if it would be a net positive for all once it is created

Most of these things can be done easilu with existing verbs.

But here is something else: client authenticating versions of existing verbs like SGET, this is my idea to kill cookies.

Browsers/clients generate signing keys like with TLS except they use either session-unique public key and identifier for anonymous access or server-registered identifiers and public key for logged in access. Each SGET is signed by the server/account specific private key.

This sort of kills passwords and cookies with one blow. Browsers and clients will have to use some library to session=init("account name") or session=init("anonymous") and just pass that session to the http library so it can do SGET/SPOST. The effect would be every HTTPS transaction would be authenticated by a specific client which eliminates many MiTM scenarios and MFA bypass techniques (you can steal public keys like cookies but private key theft would be made extremely difficult), and since you are authenticating a unique client, why do you need passwords? Just add MFA to it and you don't even have to deal with SSO and oauth! Registration just means you request an account identifer with a pubkey and if approved the server gives it an ok. The burden however is on clients and servers to maintain account information which they sort of already do in a messy non-stanrdard way which this would also fix.

What are your critiques? And can someone steal my idea please?

I use GET for read, POST for write. The rest verbs are getting eaten by firewalls and middle boxes.
DESCRIBE <verb> <path> or EXPLAIN <verb> <path> would be nice, but GraphQL already does this better.
Don’t know if this post was meant as a joke but I could see DO or CALL as useful additions. Sometimes you just want a process to run where input, if any is required, will not be persisted nor used as query filtering criteria and no query result sets are expected in return. Yes, I know there are other constructs to perform these tasks but from a pragmatic standpoint it’s simple to just throw up an http endpoint to address these businesses cases. What’s annoying is having to decide on whether to use POST or GET.
BUY is the best verb in an ecommerce producer/consumer product context, but not all payments thru the web fall under this category. Here are some examples that argue for the verb PAY:

- paying a bill to your healthcare provider - sending a donation to an organization - putting a deposit to reserve a product - renting a car