Limits.
Well developed error codes.
Rpc is a huge footgun.
What's annoying about rest is that it is a religion treated as a universal truth.
But really, rest is basically crud.
Limits.
Well developed error codes.
Rpc is a huge footgun.
What's annoying about rest is that it is a religion treated as a universal truth.
But really, rest is basically crud.
* There is no useful distinction between different kindes of bad requests (syntactically wrong, structurally wrong, not applicable to the data found, etc.)
* Special handling is needed to test if a resource exists -- you cannot just GET it and check for a 404 code because browsers log all 404 as errors, even if it's the "happy path"
* The error codes confuse authentication and authorization
These error codes are useful when there are middle-men (e.g. proxies) that can understand them without having any application-level logic. But for most REST APIs you don't want those in the way, so that point is moot.
My (to be evaluated) opinion is that RPC is underrated because there have been horrible RPC monstrosities in the past, though I'm aware that without any well-done RPC this opinion is useless.
For example, status 412 can be thought of as "try the transaction over again, your information about the resource is out of date". Maybe your client has generic support for atomic operations on resources, and 412 controls that. 429 can be thought of as "slow down". If your client has built-in throttling, 429 controls that.
> There is no useful distinction between different kindes of bad requests (syntactically wrong, structurally wrong, not applicable to the data found, etc.)
In general, a bad request is not something that client software would be expected to handle, other than "don't expect a different result if you try again". Obviously this is not a hard and fast rule because some errors are truly transient and some servers give the wrong error code due to bugs or whatever.
You could go all WebDAV with status 422 for non-syntactic errors, but I am skeptical of its utility. If your action is not applicable to the resource, you have 405.
When there's a 400 error, the expected client response is "some programmer will come by and look at the logs if it turns out to be a problem".
> Special handling is needed to test if a resource exists -- you cannot just GET it and check for a 404 code because browsers log all 404 as errors, even if it's the "happy path"
This is more of a UX issue with the browser console than any problem with HTTP.
> The error codes confuse authentication and authorization
Eh, it could be worse. We also have misspelled headers like "referer". As long as clients and semantics use the correct semantics, I don't care.
FastAPI uses 422 for "your data is structured wrong for this schema" and it's better than sliced bread.
I think the worst part about the 4xx/5xx distinction is that it doesn't really tell you whether the request is failing in a transient or permanent way (or can't tell). It would have been nice to have more leading digits (6xx, 7xx) and make some distinctions:
- the client did something wrong but might be able to succeed later if state changes (403, 404, 429)
- the client did something wrong and the result is unlikely to change without changing the request (405, 422)
Same with the 5xxs, split into "server" (endpoint) failed, vs middleware (load balancers, etc) failed or is overloaded.
403, 404—log error, maybe retry later. I don’t think there’s a reasonable way for the server to distinguish transient vs permanent failures here.
429—client should back off, throttle requests.
5xx—client should back off, throttle requests.
The big problem here is that from the server side, if you try to figure out whether an error is transient or permanent, you often get the wrong answer. It’s a diabolical problem. The distinction between “failed” and “overloaded” is something that you might figure out in a post-mortem once humans take a look, but while it is happening, I would not expect the two to be distinguished.
What I do want to transmit from server to client are things like:
- Try again at a different URL (307).
- Try again at a different URL, and update your config to point at this other URL (301, 308).
- The request should be authenticated (401). Try again if you can authenticate.
- I understood the request and had the resources to process it, but the request failed (403, 404, 405, 412, etc) due to the system state. Retry if the user asks for it.
- There is something wrong with the request itself (400, 422, etc). This is a bug, get a programmer to look at it.
- Slow down (429). Retry automatically, but not too quickly.
- As above, but take a look at the server or proxy to see if it’s working correctly. (503, 504)
- Go look at the server logs. (500)
As a rule, I would say that any error can be transitory, and I would tend to write clients with the ability to retry any request. Not as a hard rule, just as a tendency. A “permanent” status code isn’t necessarily a statement of fact, but just a belief.
That's false. The distinction is cromulently expressed by obeying the relevant RFC 9110 § 15.5.1. and 15.5.21. Further detail can be described as shown in RFC 7807 § 4.
> The error codes confuse authentication and authorization
It's not only the status codes. Like the misspelling of "referrer", this is legacy crap that cannot be changed to due backward compatibility.
> Special handling is needed to test if a resource exists -- you cannot just GET it and check for a 404 code because browsers log all 404 as errors, even if it's the "happy path"
That's analogue to "the whole world's a VAX" chestnut. Does existence of RFC ignorant software mean that we must penalise the other software that implements correctly? I think that would be a bad affair to be in. I refuse to make myself complicit in this.
> RPC is underrated because there have been horrible RPC monstrosities in the past
That's a misunderstanding of the argument in TFA. RPC on the Web is bad because the design is fundamentally worse, not a particular implementation.
Our 403s describe what permissions precisely the user is missing.
Our 400s describe what is malformed about the request.
A 404 would indicate 'not found' at the transport layer e.g. a bad proxy configuration or you didn't hit the right server at all. You definitely wouldn't confuse it with '${my_widget} not found'.
A couple of weeks ago there was an interesting post about this topic here in HN.
Why should '${my_widget} not found' be different?
So if you return 404 on this layer you are saying that whatever file or endpoint you searched is not there.
If you ask for a resource at an endpoint that does not exist e.g. via an parameter in the url and return 404 it is not clear if the endpoint does not exist e.g. the endpoint.php is missing or the resource you are looking for. Leading to questions like: is the endpoint down, is my internet wonky, did I misspell the name, and business logic related questions: is there no such widget, is the widget out of stock, should I call another endpoint?
404 does not indicate non existence but it indicates an inability to be found by the server. It's a nuance and it only matters when it does and then it bites...
Why should the error code for /product/foo/thumbnails/123.jpg be different if served with nginx serving a static file or an application server that dynamically generates it based on the product id?
But if the route is ID-agnostic, like `/cgi-bin/generate_thumbnails.pl` or `/api/json_rpc.php` then you could justify a 200 OK that the endpoint itself was found, regardless of the parameters. In this design the RPC is definitely above the HTTP level.