Hi, guys! This is my first post on Dev.to.
I come to share with you a thought that intrigues me a little. Maybe, a little strange one at first sight.
Well... I have built a REST API for a personal project and worked on a endpoint that retrives a single resource. For instance:
GET /books/8c2ba535-5523-47a5-8a72-281c316d5fc4
As you can see, the resource can be found by an UUID. This URI is mapped as GET /books/:id
.
The thing is this UUID does not represent any record in the books table in the database. So, it returns nothing.
It is common to deal with this situation returning a 404 (not_found)
status code that indicates an error from the client as specified on RFC 7231 section 6.5.4
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.
I have dealt with it as an untouchable law until reading what is specified on RFC 7231 section 6.3.1:
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;
This definition for 200
status code makes me believe that returning a payload with null
is correct because it is what exactly the client requested! The client just sent an ID that does not represent any record, the structure of the URI is fine though. Thus, the client gets a success response, but with a null
as a resource.
According to this logic, the payload would be like:
{
"data": {
"book": null
}
}
For the status code to be 404
, the request would be like:
GET /boocs/8c2ba535-5523-47a5-8a72-281c316d5fc4
boocs really does not exist.
All of this makes me think the way we handle this situation depends on how we deal with the role of the database in the application.
Does the database have an active participation on the application interfering in the business rule? Go for 404
status code. Eg.: a private repository hosted on Github being accessed by an unauthorized user.
Is the database just a data repository? I tend to believe the response would have a success payload with null
. Eg.: the majority of the APIs worldwide.
Summing up:
"If you have nothing to give, give nothing. If you do, decide if you want to give something, (don't) give it and communicate."
Crazy, uh?
I know there already are some talks about this on the internet, but I think it worths sharing my thoughts with you and I'd like to know about your opinion.
What do you think about?
Top comments (19)
I think the "principle of least astonishment" would dictate
404
to me personally, but this is definitely a juicy topic!IMO 404 all the way 100% o long as we're talking about REST.
The whole REST architectural style is predicated on the idea that the state of a resource at any given moment is temporary:
Source: Fielding dissertation 5.2.1.1
The idea of the current resource state is hinted at in the immediately following line of the referenced RFC 7231 section 6.5.4
In other words: the resource does not exist in this API at this moment. However, it might one day. Who knows. But it is 404 Not Found right now.
Reading just one sentence further in the definition of 404 in RFC 7231, you find out that there's a response code for if the resource was there at one point, and is now permanently no longer there:
This indicates the shared understanding of the author of the RFC of a resource as the mapping to an entity. The entity changes, it has state, but the mapping does not. The mapping identifies that entity permanently.
So to address the issue raised in the above article:
The main reason I believe this is not an accurate interpretation is that the mapping does not exist. And since the resource is the mapping, 404 is the only logical decision.
Besides that, there's already an implicit understanding that if you receive a response, "the structure of the URI" is fine.
Finally, at the end of the day, I think it logically doesn't make sense to tell API consumers 204 No Content or 200 with a null payload, because you would make them think you do have a resource for that given ID within your API, but you're just storing all null values.
This semantic confusion would create similar issues to hash table data structures in some languages that return null values for keys that aren't in the table. Does the null response mean that the key is in the table and currently has the value null? Or does it mean the key isn't in the table? Same problem, and best to avoid.
AHHHH I’ve flipped my opinion on this so many times just on this page!! I have no idea, I think either is ok as long as an API is consistent it’d be annoying if one endpoint returned a 404 if the record didn’t exist and another endpoint in the same API returned a 200 with null data.
I think as long as they’re consistent either is totally fine. I think. Or do I?! I just don’t know anymore 🤯
Why add the burden of checking whether the response comes back with a null or not to the developer that uses your API?
501 Not Implemented or 405 Not Allowed, for endpoints that do not exist (boocs, in your case)404 Not Found for resources that do not exist (or the user has no access to)
200 OK for succesful queries that return data
If the database is part of your application, go ahead with 404. If not, why to lean your app on it? It's just another perspective of how to look at the application. The RFC keeps ruling.
If a resource is requested with an invalid ID, then either:
I would be very wary of responding with a 2xx code, as the caller may well assume they have what was asked for and misbehave.
That said, I recommend RFC7807 error messages should always accompany an error response, to add machine-readable nuance that status codes do not permit: tools.ietf.org/html/rfc7807 :)
Returns a 404, because the URI points to something which does not exist
Returns a 200, with a null response because it's a query for a record which does not exist.
Returns 400 with this body
Going to disagree with this as it's a leaky abstraction, exposing the unnecessary detail of having a database to the calling client.
Whether or not a database is queried to check if the book in fact exists or whether all books are simply static pages (though that's often not feasible) should not actually matter to the client. Unless the API is able to reply the other path of
GET /boocs/8c2ba535-5523-47a5-8a72-281c316d5fc4
with a similar response such asthen I don't think there's actually full consistency and a clear abstraction being made
404 all the way. It's client error because it didn't supply correct ID.
If a message is needed, RFC suggests using object type called problem details. It is standard type to transfer error messages and it can be extended to contain additional information 🙂
My rule of thumb is that an endpoint should only have a single successful response type and code.
The docs say: developer.mozilla.org/en-US/docs/W...
So Sorin's comment 204 - no content kind of makes sense.
Then i think, well what if that book exists, but is just currently not available?
Getting a blank "no content" doesn't tell you what's actually wrong with your request or with the data.
Ben's note about a 404 is also what's often taught these days as far as error responses go and you can put a message in the response header itself [which almost everyone i know doesn't even know is possible so they completely overlook it]
Personally, I hate it when an endpoint returns a generic response header with no message on error.
*Because you don't know if the endpoint is failing, or you passed in the wrong path, or the data requested doesn't exist. *
I much prefer a 200 response: The request has succeeded
[so you know the endpoint works and received your info correctly]
It should respond with a response object with status: failed and msg: "No go buddy: because x y z" that tells you exactly what's wrong with your request info, since the server was successful in processing it and respond.
This way you know what to do to fix it.
What you're looking for is called "204 - No Content"
Hi Sorin Costean!
I don't think so. If you have an endpoint to get a list of books and make a request to it. What would it be that response? A empty array with
200
status code or204
with no content? I think the first response fits more the client expectations than the second one. Why would it be different for a single resource?Marco, what i usually do is
If the request came on a list endpoint and there's no content i return
200
with a empty list, if the request is for a single record and the record doesn't exist i return204
.Normally my "pagination" responses retrieve another useful data as well like "next_page", "count" and etc, so it makes easy for validation if the response is "200".
An empty list is no content as well. Why not use 204 for it?
If you have no items for the list, returning an empty list will yield the full available content. Maybe you will also add some metadata (e.g. total number of entries) to support pagination. Your consumer will not have to treat this differently from a filled list. When requesting a single item that does not exist, users would have to examine the content and see if they can work with it. And from my experience: if you start seeing surprising responses, the trust in the responses from this system drops rapidly.