How to say a REST-ful NO?
We had an interesting discussion at work yesterday: for a REST api, what HTTP status code to return when a resource does not exist - a 200 OK with an empty result, a 204 NO CONTENT or a 404 NOT FOUND ?
A lively discussion resulted in a flurry of ideas and only one near-fatality. We figured it all boiled down to what the client is asking for.
(1) If the client is requesting for a resource, it is either found (200) or not found (404).
GET /dogs/1
200 OK
{ "breed": "pomeranian", "type": "annoying" }
GET /dogs/991
404 NOT FOUND
{ "error": {"message": "dog '991' not found", "requestId": "x124jhakas"} }
(2) If the client is searching for a resource and the query is valid, you either get results (200 with a collection) or you get no results (200 with an empty collection).
Here, the client receives a 200 in both outcomes, implying that the query was valid but nothing matching it was found. This is slightly better than returning a 204 because we might not want to send an empty response body that the client will have to handle differently (think NULL object pattern).
GET /dogs?type=annoying
200 OK
{ "total": 2, "results": [{ "id": 2, "breed": "chihuahua" }, { "id": 1, "breed": "pomeranian"}] }
GET /dogs?fly=true
200 OK
{ "total": 0, "results": [] }
There’s an interesting offshoot to this. What do these (arguably same) requests do?
/dogs/991
404 NOT FOUND
/dogs?id=991
200 OK
If we follow the logic discussed above, we’ll get a 400 for the first request and a 200 for the second. This actually makes sense if you see the object-vs-query distinction. Another view is that the second url should be redirected to the first (with a 3xx status). IMHO, that’s a bit inconsistent, i.e., what happens if you have more than one query parameter, i.e., /dogs?id=991&annoying=true?