17 Jul 2024
Be Clear When Responding “Not Found” in REST Endpoints
When it comes to REST endpoints, I have been amazed at the conflicting and bad practices employed for response codes. Specifically the use of the response codes:
- 404 (page not found)
- 204 (content not found)
- 400 (Bad Request)
The HTTP response codes are a very rich and detailed set of answers. But there is a very strange practice, when it comes to 404 and 204 for endpoint responses. I’ve seen it degrade into a soapbox debate at times.
- Many people argue that 204 is only a valid response for a PUT action: the object to update is not found.
- 404 is well-known as Page Not Found, so sometimes it’s just “use 404 because that means it does not exist.” Often the person arguing this way is not even aware of the other “2xx” successful result codes beyond 200.
There is a well-known meme which can help visualize the confusion, and its cause. It’s known as Car Not Found.
People appreciate this example if they have ever seen a 404 page not found error page, and there is an underlying lesson in the picture which applies to what I am saying, because the picture kind of epitomizes the thought process which causes the problem.
The picture shows that at spot 404, there is no car. Is this problem an absence of a car, or an absence of a parking spot.
Now for this example, let’s imagine that instead of 404 being the spot number, B17 was the spot number. This is often the case in a parking lot for rental cars. In the programming world, there are a number of endpoints which report no car at B17 as a 404 (Not Found Error). But what if spot B17 did not exist (e.g. the last spot in row B was B15). Does responding with 404 for spot B17 mean:
- There is no car at B17.
- There is no spot labeled B17.
… and when troubleshooting starts, this is where the ambiguity creates a lot of headaches when the only response code is 404. So I am a strong advocate of returning 204 for not only PUT and DELETE calls, but also on GET calls. Note: there is no restriction in the W3C documents I have found which limits the use of 204 to only a PUT method. The Mozilla specification for 204 is worth reading.
Another thing to consider is that 404 represents a non-existent resource, which can also mean a static resource–even when the path to that resource is built from path parameters. So something interesting can happen when path parameters are being used. Let me give you a scenario to consider.
Say that my endpoint is /car/find/{spot}, where {spot} would be passed as /car/find/B17. The spot exists, so my response could be 200: “Toyota”, etc., or 204 for nothing there. This works fine.
Now say that I have a regular expression tied to the {spot} argument on the path. It allows a single-letter followed by two digits, so I have to send B09 and not B9. If I make a call to
/car/find/B9, which violates the regex validation, what is the more appropriate answer:
- 404 (Not Found)
- 400 (Bad Request)
In the case where the path parameters are static (not a variable resolved from the content), then 404 is the only answer. But in our example, while B9 as an argument is incorrect, the spot does exist–it just has to be represented as B09 for the endpoint to accept it. This generally occurs with unrectified input from the source.
The issue here is more of a 400 (Bad Request) problem, but it takes code in the middleware handing the path routing to test the path without arguments for a clear answer whether the processing endpoint itself is actually present. If it is, the 400 (Bad Request) is a better response because the issue is not with a missing endpoint, but a value in the path (an argument) which is invalid. Response Code 400 is described in the Mozilla spec as “… indicates that the server cannot or will not process the request due to something that is perceived to be a client error…).
An invalid path argument should qualify for a 400 response, since it is part of the request and not the path. You can write some quick tests on platforms to see what you get back for this. 404 is common for this case. I understand that using platforms for your end point routing (Mulesoft, etc) will dictate your answers to this, since that code is theirs.
I have seen a lot of implementations where a successful call (not necessarily successfully processing) always returns 200, and a JSON object which has admin wrapper properties to describe the success/failure of the processing, and some details about the error and any special processing steps. This is OK, because the REST spec is not this restrictive, but I often wonder how much of this evolved from a lack of understanding the existing response codes and what they communicate.
Don’t get me wrong: I love REST. I just pass this along as food for thought.
Leave a Comment
You must be logged in to post a comment.