{"id":3036,"date":"2024-07-17T11:50:15","date_gmt":"2024-07-17T15:50:15","guid":{"rendered":"https:\/\/blog.bitsofgenius.com\/?p=3036"},"modified":"2024-10-23T10:45:12","modified_gmt":"2024-10-23T14:45:12","slug":"be-clear-about-response-for-not-found-in-rest-endpoints","status":"publish","type":"post","link":"https:\/\/blog.bitsofgenius.com\/?p=3036","title":{"rendered":"Be Clear When Responding &#8220;Not Found&#8221; in REST Endpoints"},"content":{"rendered":"<p>When it comes to REST endpoints, I have been amazed at the conflicting and bad practices employed for response codes.\u00a0 Specifically the use of the response codes:<\/p>\n<ul>\n<li>404 (page not found)<\/li>\n<li>204 (content not found)<\/li>\n<li>400 (Bad Request)<\/li>\n<\/ul>\n<p>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.\u00a0 I&#8217;ve seen it degrade into a soapbox debate at times.<\/p>\n<ul>\n<li>Many people argue that 204 is only a valid response for a PUT action: the object to update is not found.<\/li>\n<li>404 is well-known as Page Not Found, so sometimes it&#8217;s just &#8220;use 404 because that means it does not exist.&#8221;\u00a0 <em>Often the person arguing this way is not even aware of the other &#8220;2xx&#8221; successful result codes beyond 200.<\/em><\/li>\n<\/ul>\n<p>There is a well-known meme which can help visualize the confusion, and its cause. It&#8217;s known as Car Not Found.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/blog.bitsofgenius.com\/images\/CarNotFound404.png\" alt=\"Where did my car go?\" \/><\/p>\n<p>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.<\/p>\n<p>The picture shows that at spot 404, there is no car.\u00a0 Is this problem <em><span style=\"text-decoration: underline;\"><strong>an absence of a car<\/strong><\/span><\/em>, or <em><span style=\"text-decoration: underline;\"><strong>an absence of a parking spot<\/strong><\/span><\/em>.<\/p>\n<p>Now for this example, let&#8217;s imagine that instead of\u00a0<span style=\"text-decoration: underline;\"><strong>404<\/strong><\/span> being the spot number, <span style=\"text-decoration: underline;\"><strong>B17<\/strong><\/span> was the spot number.\u00a0 This is often the case in a parking lot for rental cars.\u00a0 In the programming world, there are a number of endpoints which report no car at <strong>B17<\/strong> as a 404 (Not Found Error).\u00a0 But what if spot <em><span style=\"text-decoration: underline;\"><strong>B17<\/strong><\/span><\/em> did not exist (e.g. the last spot in row B was <span style=\"text-decoration: underline;\"><strong>B15<\/strong>)<\/span>.\u00a0 Does responding with 404 for spot <span style=\"text-decoration: underline;\"><strong>B17<\/strong><\/span> mean:<\/p>\n<ul>\n<li>There is no car at B17.<\/li>\n<li>There is no spot labeled B17.<\/li>\n<\/ul>\n<p>&#8230; and when troubleshooting starts, this is where the ambiguity creates a lot of headaches when the only response code is 404.\u00a0 So I am a strong advocate of returning 204 for not only PUT and DELETE calls, but also on GET calls.\u00a0 Note: there is no restriction in the W3C documents I have found which limits the use of 204 to only a PUT method.\u00a0 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Status\/204\" target=\"_blank\" rel=\"noopener\">The Mozilla specification for 204 is worth reading.<\/a><\/p>\n<p>Another thing to consider is that 404 represents a non-existent resource, which can also mean a static resource&#8211;even when the path to that resource is built from path parameters.\u00a0 So something interesting can happen when path parameters are being used.\u00a0 Let me give you a scenario to consider.<\/p>\n<p>Say that my endpoint is \/car\/find\/{spot}, where {spot} would be passed as \/car\/find\/<span style=\"text-decoration: underline;\"><strong>B17<\/strong><\/span>.\u00a0 The spot exists, so my response could be 200: &#8220;Toyota&#8221;, etc., or 204 for nothing there. This works fine.<\/p>\n<p>Now say that I have a regular expression tied to the {spot} argument on the path.\u00a0 It allows a single-letter followed by two digits, so I have to send\u00a0<span style=\"text-decoration: underline;\"><strong>B09<\/strong><\/span> and not\u00a0<span style=\"text-decoration: underline;\"><strong>B9<\/strong><\/span><span style=\"text-decoration: underline;\">.<\/span>\u00a0 If I make a call to<\/p>\n<p>\/car\/find\/<strong><span style=\"text-decoration: underline;\">B9<\/span><\/strong>, which violates the regex validation, what is the more appropriate answer:<\/p>\n<ul>\n<li>404 (Not Found)<\/li>\n<li>400 (Bad Request)<\/li>\n<\/ul>\n<p>In the case where the path parameters are static (not a variable resolved from the content), then 404 is the only answer.\u00a0 But in our example, while <span style=\"text-decoration: underline;\"><strong>B9<\/strong><\/span> as an argument is incorrect, the spot does exist&#8211;it just has to be represented as <strong><span style=\"text-decoration: underline;\">B09<\/span><\/strong> for the endpoint to accept it.\u00a0 This generally occurs with unrectified input from the source.<\/p>\n<p>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.\u00a0 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.\u00a0 Response Code 400 is described in the Mozilla spec as &#8220;&#8230; indicates that the server cannot or will not process the request due to something that is perceived to be a client error&#8230;).<\/p>\n<p>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.\u00a0 404 is\u00a0 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.<\/p>\n<p>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.\u00a0 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.<\/p>\n<p>Don&#8217;t get me wrong: I love REST.\u00a0 I just pass this along as food for thought.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When it comes to REST endpoints, I have been amazed at the conflicting and bad practices employed for response codes.\u00a0 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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,13,17],"tags":[],"class_list":["post-3036","post","type-post","status-publish","format-standard","hentry","category-technologynetworking","category-technologythoughts","category-tips-and-tricks"],"_links":{"self":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/3036","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3036"}],"version-history":[{"count":19,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/3036\/revisions"}],"predecessor-version":[{"id":3132,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/3036\/revisions\/3132"}],"wp:attachment":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3036"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3036"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}