204 is quite tricky. The RFC says
The server has fulfilled the request but does not need to return an entity-body"
So there must be a request to fulfill and there is no need to return anything. This is not the case here. For a GET
request the server needs to return a representation of the resource if its there. So either the resource is no there or it returns it. It cannot say "The resource is boring. I won't return it. Guess how it looks like".
Lets have a look at the response codes which come into consideration:
- 200 OK: the resource logically exists and a representation is returned in the body
- 204 No Content: the request is fulfilled and there is nothing to return
- 404 Not Found: the requested resource does not exist
These are all similar but there are subtle differences as there are different kinds of "nothing" to return.
Maybe you have a resource like /messages
and a GET
returns obviously the list of messages. This list can be empty: []
(or for security reasons rather {"messages": []}
). Returning an empty list is fine and results in a 200. A huge benefit of empty lists compared to empty results (corresponding to null) is that the client can often handle an empty list using the same piece of code as it handles a non-empty list. No additional logic is required which reduces complexity and the possibility for errors.
Maybe there is logically nothing to return. This typically occurs when you have a PUT
, POST
, DELETE
, or PATCH
action. In these cases you may return a body (the old/new entity) but you needn't. When you do not, return 204.
Maybe the resource is not empty but does not exist. In this case you should return 404.
As an analogy think of resources as files (resources don't need to be files but they can and that's quite common for static HTML which happens to be the origin of HTTP): 200 means the resource/file is there but may be empty. 204 means the service is not obliged to return a file. 404 means the server was requested to return a file but there is no file to return.
So in your example GET /messages/next
should return 404 when there is no next message and return 200 if there is a next message but this message is empty. It should never return 204.
When using HATEOAS the resource /messages/next
should only be linked iff there is a next message.
Alternatively you could have a resource /messages/newest
and request it using a conditional GET
request (e.g. using the If-Modified-Since
header).
Also have a look at this nice overview.