Some good points - particularly about not returning arrays (I've made that mistake!)
But I feel 410 instead of 404 is pretty controversial:
> There are many layers of software that can return 404 to a request
Anything in your stack can return any HTTP error code - I don't see why 404 is special.
> When calling (say) GET /things/{thing_id} for a thing that doesn't exist, the response should indicate that 1) the server understood your request, and 2) the thing wasn't found. Unfortunately, a 404 response does not guarantee #1.
The server is free to return other codes for other classes of problems. The server could return 400 for a bad request, and leave 404 for "thing wasn't found", indicating it understood the request but it wasn't found.
Also surprised not to see RFC 7807 / RFC 9457 (Problem Details) not mentioned in the "structured error format" section.
> Anything in your stack can return any HTTP error code - I don't see why 404 is special.
I'm surprised you don't - in my experience 404's are by far the most common response to get when you haven't wired things up correctly. Sure anything in the stack _can_ return any code and response they want, but you're still much more unlikely to come across a 410 rather than 404. If that unlikeliness saves you support calls down the line then that's pretty good.
With REST 404s due to missing resources (non-existing ID), you should generally get corresponding error information in the body (as also described in TFA), and clients should log/display that information. That should make enough of a difference. There’s a lot of “should” here, of course, but instead of teaching developers to not use 404, it would be better to teach them to create and handle error responses appropriately.
I really feel like you're ignoring the whole point, which is that this is about helping you out when things go wrong. Yes, it'd be great if we could teach people to never write bugs and that they should always check the response body, but that's just not what happens in reality. Being able to immediately differentiate the problem based on the response code is very useful when you get a bug submitted from someone saying they get a 404 and it doesn't work.
There are two reasons behind 404 being a bad response code to use for empty results.
- Did I get a 404 on this endpoint because the endpoint doesn't exist? Or did I get that because the object I was looking for doesn't exist? Great, I need to dig into the response body to find out, indicate that I can either get a 200 or a 404 with this endpoint, and deal with the odd case where the API returns HTML regardless of the MIME type in the Accept header if the endpoint itself is not there because "fuck you, couldn't be bothered".
- Some HTTP libraries will consider anything that's not a 1/2/3xx an error. That can be annoying to deal with.
If anything that's not 1/2/3xx is a problem then 410 won't be a solution. And I doubt that your http library having issues to handle 4xx can handle 1xx correctly.
> You could use 404 but return a custom error body and demand that clients check for a correct error body. This is asking for trouble from lazy client programmers. It might or might not be "your fault" when clients see eventually inconsistent data, but the support calls they send you will be real.
Make sure that your 404 responses were always documented, then tell them to RTFM.
The problem is that the support call comes in as "your DELETE call isn't actually deleting". Sure it's not your fault, but it imposes a cost on you to investigate. And of course the first time you go directly to RTFM without checking will be the time it actually is your bug.
404 is special because it's so incredibly common. Why take the risk? There are other perfectly good error codes that - in practice - don't have this issue.
I prefer to consider 404 as protocol error and missing thing as business error. That way 404 signals wrong endpoint and 200 + error message + empty result set signals wrong id.
Or even more simple: Anything other than 200 means check infrastructure docs and if you don't like the 200 check the business requirements.
As a client I generally dislike APIs that use 200 for error conditions. The problem is that API implementors often change the structure of the response.
GET /thing/THG123
# on success:
{"id":"THG123", "name":"thingie"}
# on failure:
{"error":"no such thing"}
Working in typed languages, this requires parsing the response, determining success or failure, then reparsing the response into the appropriate type. Annoying.
Of course it's not always like that, some APIs will put both the error and data in a wrapper object and one field or the other will always be null:
This is less annoying but it's still tedious. We could eliminate the wrapper if we only had an out-of-band signal to indicate whether the client should expect a success response or an error response... like maybe an HTTP status code? I mean, it's right there, why not use it?
Yes. The TFA argument is about top-level information, for example when paging through a collection. Personally I think HTTP headers could fit the purpose for such meta information.
But I feel 410 instead of 404 is pretty controversial:
> There are many layers of software that can return 404 to a request
Anything in your stack can return any HTTP error code - I don't see why 404 is special.
> When calling (say) GET /things/{thing_id} for a thing that doesn't exist, the response should indicate that 1) the server understood your request, and 2) the thing wasn't found. Unfortunately, a 404 response does not guarantee #1.
The server is free to return other codes for other classes of problems. The server could return 400 for a bad request, and leave 404 for "thing wasn't found", indicating it understood the request but it wasn't found.
Also surprised not to see RFC 7807 / RFC 9457 (Problem Details) not mentioned in the "structured error format" section.