One team got a stream of 404’s not because the requested objects did not exist in the DB, but because the service moved. Web server was saying: resource does not exist.
Another time, the server had the resource but didn’t like the state of the data, so refused to serve it. Debate ensued as to whether this was a 400 or 500 class error. People got religious.
Yes there’s an answer but it should be so obvious that we don’t have the debate. This isn’t sophisticated verbs, both happened with GET.
TL;DR - API developers should make it so consumers have the necessary information and tools to know what's happening. If you're just returning a 404 with no other info, you have a bad API.
There's basic error handling/reporting that seems to transcend technology and architecture, and a big part of that is that errors should have unique error codes. In the context of a web API, both "route not found" and "resource does not exist" should return a 404, but each should have a unique `code` in the body:
{"code": 0, "err": "route not found"}
{"code": 1, "err", "user not found"}
For HTTP, the status code is often for general application development, and the error code is for debugging, though there can be overlap and it's completely fine if a client wants to implement custom logic based on `body.code`.
A validation error should look like:
{"code": 2, err: "invalid", "data": {
"username": [{"code": 100, "err": "required"}],
"password": [{"code": 101, "err": "must be at least 6 characters", "data": {"min": 6}}]
}}
The `code` always indicates what other data, if any, exists. Above, a code of 2 means there'll be a `data` field of errors. A validation error of `101` means there'll be a `min` field in `data`. `err` is an user-safe (developer friendly) description of the message which can always be regenerated from `code` + `data`.
For errors that aren't known ahead of a time (e.g. a server error), that should also have a distinct code, say 500, and the "data" field should contain an `error_id` which can be used to look up the error.
> In the context of a web API, both "route not found" and "resource does not exist" should return a 404, but each should have a unique `code` in the body
I forget if it was 404 or something else, but you should check if it actually works first. One of our sites did exactly as you suggest here, and it worked totally fine in development (django "runserver"), but didn't work in production (wsgi behind apache). Turned out with that HTTP code, apache was discarding the body.
One improvement I'd steal from theirs and drop in yours - constant (or enum) string codes. It's a lot more scannable when debugging/reviewing/maintaining than having to look up integer codes in a table.
Just pointing out that codes make it easier to provide translations and ability to switch between error types with more confidence than string matching.
Error codes should be accompanied by helpful messages so you don't have to look up the table.
Yeah, from what I understand 400 means "fix it at the client's end" and 500 means "fix it at the server's end" which seems to be what the RFC thinks, too:
Basically, 4xx is telling the client you did something wrong (such as visit a non-existent URL), 5xx is when the server errors and can't respond appropriately.
Obvious to you and me. Server team disagreed and tried 422 unprocessable entity, which is not correct. My point is that these issues should be clear enough that there are not thousands of StackOverflow questions on it, and debates between teams. “It’s so easy” is not true. There are layers of understanding.
Another time, the server had the resource but didn’t like the state of the data, so refused to serve it. Debate ensued as to whether this was a 400 or 500 class error. People got religious.
Yes there’s an answer but it should be so obvious that we don’t have the debate. This isn’t sophisticated verbs, both happened with GET.