It's the difference between "There is nothing handling this route at all" and "there is something handling this route but the object isn't found." To me the former is an HTTP 404 and the latter is an application "Not Found."
But I also very much don't like playing the "if you expose your app over HTTP you should assimilate HTTP semantics and do a fuzzy lossy map of your application to HTTP verbs and HTTP status codes." I leave the HTTP semantics to the front end web server and live on top.
> It's the difference between "There is nothing handling this route at all" and "there is something handling this route but the object isn't found."
'Nothing handling this route' has no meaning, because routes have no meaning in a hypertext application. Clients should not generally be constructing URLs by hand; they should be receiving them from the servers they communicate with.
In the example in the article, an application dealing with employees should not be constructing URLs by appending employee IDs to strings; rather, every reference to an employee in the application should be to a URL rather than an ID. So when it requests a list of employees, it receives the equivalent of {/api/v1/employees/1, /api/v1/employees/2 … /api/v1/employees/N} rather than {1, 2 … N}.
> I also very much don't like playing the "if you expose your app over HTTP you should assimilate HTTP semantics and do a fuzzy lossy map of your application to HTTP verbs and HTTP status codes."
If you are building a hypertext application, then you should build a hypertext application. It's completely possible. Off the top of my head, protocols such as ACME (used by Let's Encrypt) are good examples to follow.
Yep, there's absolutely no reason an application ought to be guessing URLs, and therefore no reason it should ever be requesting /api/v1/employees/69 where employee 69 doesn't exist. If it does, it's playing silly buggers exactly as much as if it were to request /api/v69/nice/. Any user resources it needs to access will have had URLs provided to the application by another page.
users = http.client.get("https://yourapi.com/users")
# Frobulate each user
for user in users:
user = http.client.post(user.frobulate_url)
# While this is running somewhere on the other side of
# the world someone deletes one of the users. Oops.
Assuming, users (i.e developers) will just never mistype a URL so you don't have to give useful feedback is just like being a bad netzen.
That's a fair example. And returning 404 (Not Found) or 410 (Gone) is the most useful kind of feedback. More explanation in the body of the 404 response is helpful too, of course.
> Clients should not generally be constructing URLs by hand
I'm not sure I get this, every API doc is like "go to /users for the users and here's the methods we support, the payloads, and responses" If someone mistypes it and tried to get "/user" I want to send a 404 to be like "there is nothing here."
> In the example in the article, an application dealing with employees.
This is all very nice when your domain maps nicely to objects. My litmus tests for this is what would the semantics of mysql.net/query?db=mydb,q='select * from table;' look like.
* If the result is an empty record set should it return 404? Ew. I think it should be 200 with the response being `[]`.
* If you're not allowed to access a table should you get 403?
"Select *" is like find/ search - any API that could logically match multiple entities and return a list should stick to 200 in the case of an empty set.
But for an API designed to return just the specific single resource requested there's a decent case for a 4xx status code if it doesn't exist (400/404/410/422 all being justifiable depending on your preferences).
But I also very much don't like playing the "if you expose your app over HTTP you should assimilate HTTP semantics and do a fuzzy lossy map of your application to HTTP verbs and HTTP status codes." I leave the HTTP semantics to the front end web server and live on top.