Returning 200 and then using response body to denote missing resource is no different. So you have to choose, either 404 can be invalid path and missing employee; or 200 can be valid data or missing employee. Personally I would prefer the former as 200/OK then indicates success.
404 Not found is not exactly correct. I argue that the server found a representation of the object with that representation being "It does not exist".
If you try to request a file `/public/pdfs/100.pdf` and that does not exists what does the server respond? 404
What the server responds if you try `/public/dpdfs/1.pdf`? Still 404 as that path does not exists on the local storage.
What is the difference for a client if 100.pdf should be an actually file or a data stream served from a web framework? There should be no difference.
Choosing to behave when building a dynamic app the same way as the static helps a lot with integrating with multiple other services (eg. caching, observability ...)
>The 200 (OK) status code indicates that the request has succeeded. The payload sent in a 200 response depends on the request method. For the methods defined by this specification, the intended meaning of the payload can be summarized as:
>GET a representation of the target resource;
>The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
If you to give the client the representation of the target resource (i.e. it doesn't exist) then send 200 and a body indicating it doesn't exist.
If you want to tell the client you couldn't find a representation for the target resource then send 404
Let's say that you have a nested resource/path that will return devices owned by the employee so you will have this path:
`GET /api/v1/employees/1/devices/1` and this might return the first device owned by the employee.
Now what do you think the following request should return:
`GET /api/v1/employees/100/devices/1` where the employee with ID 100 does not exists but there is a device with the ID 1 owned by some other employee?
or
`GET /api/v1/employees/100/devices/1000` where both the employee with id 100 and device with id 1000 does not exists?
What do you think the response should be? Still 200?
So you're asking for the device with id 1 owned by employee 100. The answer is that the device exists but is not owned by employee 100 because there's no employee 100. So return 200 plus however you want to represent "the device exists but is not owned by employee 100 because there's no employee 100".
>`GET /api/v1/employees/100/devices/1000` where both the employee with id 100 and device with id 1000 does not exists?
Same as above but subbing in "because employee 100 and device 1000 don't exist" as appropriate
Nonexistence is not a representation of the target resource. A resource that does not exist does not have a representation.
It means that the server did not find a current representation of the resource and that the server accepts responsibility for providing an authoritative answer to whether it exists (if the latter is not the case, the most correct response is 421.) Aside from that and the combination of being unwilling to provide a default representation, not having a representation consistent with the Accept header provided by the client, and preferring not to distinguish this case from non-existence with a 406 response (which, like the situations to which 421 applies, is an edge case), the reason for a resource not being found is overwhelmingly that it does not, in fact, exist.
It is true that there are some other things that a 404 might mean, but “does not exist” is not only within the space of things covered by “Not Found”, it is by far the most common reason for it.
The problem is that this error then overlaps with server path routing issues, DNS problems, and general network issues. Even if it's logically correct it makes dealing with your API annoying.
As others have mentioned, you can return anything you want in the body of a 404 to clarify. However the other issues you all mentioned should really be in the 500s with errors.
This isn't how the world works though. Much development happens on COTS platforms or internal frameworks or whatever. Many of those will have generic error handling, logging, alerting, etc. based off the response code.
Your pedantically correct 404 with a clarifying body ruins my day (really weeks) because now I have to chase whoever built those things to fix my problem. I am annoyed and curse your name.
One topic the article didn't even touch was flagging bad parameters in the request which didn't make sense on the application level. HTTP has 422 for that. I commonly write things like
validate_my_param($c->req->params->{format})
or $c->detach(HTTP => 422, ['Invalid format']);
(the HTTP view renders that string either as text/plain or json as {"message":"Invalid format"} depending on the accept header of the request.)I've used that for years and been quite happy with the downstream results.
Edit: I should elaborate on the downstream results.
On the javascript side, the ajax methods often contain separate success and failure callbacks. You often need to handle the failure callback to ensure the user knows that something broke. If you also have an error path in the success callback, it clutters the code.
ajax({
success(data, ...) {
if (data.wasActuallyAnError) {
// lines
// of code
// to handle
// the error
}
else useTheData(data);
},
error(response, ...) {
// lines
// of code
// to handle
// the error
}
})
vs. ajax({
success(data, ...) {
useTheData(data);
},
error(response, ...) {
// lines
// of code
// to handle
// the error
}
})
This problem is not insurmountable of course. you could wrap up your error code in helper functions and reduce the boilerplate. You could write a completely generic error handler for your front-end framework and automatically include it in all ajax calls. You could also write a custom ajax method that interprets the 200 status error messages and diverts to the error callback. But I still think the 404/422 status code is a better pattern to start from, since most of the client-side frameworks I've used switch code paths based on the status code.Oh sure. I'll just call up the CTO and tell them to scrap the product they spent $10 million on because it's not pedantically correct in parsing HTML codes.
Yes, and then parse the appropriate response payload, in the context of the response code. Thats how eg. you get validation errors for a submitted form that just failed submission. If you're just assuming that everything !=200 is an error, it is your own assumption, not a framework shortcoming. As an example, most of the complex API systems I've developed would actually ignore completely the response code (so, basically the opposite of what you assumed), because the responses need to have further context information in case of errors, such as application-specific error code, context-specific error messages (eg. form field errors) or just translation-aware detailed error messages.
On a vaguely related note, most people working with HTTP API implementations seems to forget that GET requests can in fact have a request body. Most high-level clients/libraries will assume you won't use it, but it doesn't mean you can't use it.
No webserver will ever return a 404 for any of those cases.
The problem is that this error then overlaps with server path routing issues, DNS problems, and general network issues. Even if it's logically correct it makes dealing with your API annoying.
>Nobody is stoppping anyone to add response body to a 404 to indicate which nested resource is invalid.
But then we've lost standardization which is the whole point of the error codes to begin with.